1 Introducción

Este documento es la memoria de nuestra práctica para la asignatura Aprendizaje Computacional de la Mención en Computación del Grado de Informática de la Universidad de Murcia.

El proyecto consiste en el estudio de diferentes modelos de aprendizaje automático. Para ello, se ha utilizado la base de datos Credit Approval de UC Irvine Machine Learning Repository, que contiene información sobre la concesión o denegación de créditos bancarios.

En primer lugar, se ha llevado a cabo un análisis exploratorio de la base de datos. Hemos identificado atributos numéricos y categóricos y hemos renombrado las columnas según la información que hemos encontrado en un repositorio de Kaggle. Hemos proseguido con una serie de análisis monovariable y multivariable discriminando entre créditos concedidos y denegados. Esto nos ha permitido hacernos una idea de la relación entre los diferentes atributos. Para cerrar esta sección, hemos probado un Análisis de Componentes Principales (PCA) para tener una referencia sencilla con la que comparar los modelos de aprendizaje.

Después del análisis, hemos probado diversos modelos de clasificación supervisada utilizando el lenguaje R y la librería caret, aplicando técnicas de preprocesado, ajuste de hiperparámetros (((y evaluación cruzada))). El dataset se ha dividido en datos de entrenamiento y datos de test para comprobar la eficacia de los modelos. En total, se han probado cuatro algoritmos representativos de distintos paradigmas de aprendizaje automático.

2 Carga y preparación de datos

Comenzamos instalando las librerías de R necesarias para la elaboración de la práctica: - caret (Classification And REgresion Training): paquete para entrenar modelos de machine learning en R. Facilita, entre otros, el preprocesado de datos, la división de conjuntos de train/test, el ajuste de hiperparámetros…

También cargamos el dataset y vemos su contenido. Observamos una tabla de 690 filas y 16 columnas. Las columnas V1, V4, V5, V6, V7, V9, V10, V12, V13 y V16 contienen caracteres, lo que nos indica que son categóricas. El resto de columnas son numéricas.

if (!requireNamespace("caret", quietly = TRUE)) {
  install.packages("caret")
}

if (!requireNamespace("randomForest", quietly = TRUE)) {
  install.packages("randomForest")
}
if (!requireNamespace("doParallel", quietly = TRUE)) {
  install.packages("doParallel")
}
if (!requireNamespace("tidyverse", quietly = TRUE)) {
  install.packages("tidyverse")
}
if (!requireNamespace("ggplot2", quietly = TRUE)) {
  install.packages("ggplot2")
}
if (!requireNamespace("gridExtra", quietly = TRUE)) {
  install.packages("gridExtra")
}
if (!requireNamespace("GGally", quietly = TRUE)) {
  install.packages("GGally")
}


library(caret)
library(tidyverse)
library(ggplot2)
library(gridExtra)
library(GGally)

# Cargar dataset
url <- "https://archive.ics.uci.edu/static/public/27/credit+approval.zip"

# Download and unzip the dataset
temp <- tempfile()
download.file(url, temp)
unzip(temp, exdir = "./credit")
unlink(temp)  # Remove temporary file

credit <- read.table("./credit/crx.data", sep = ",", na.strings ="?")

summary(credit)
##       V1                  V2              V3              V4           
##  Length:690         Min.   :13.75   Min.   : 0.000   Length:690        
##  Class :character   1st Qu.:22.60   1st Qu.: 1.000   Class :character  
##  Mode  :character   Median :28.46   Median : 2.750   Mode  :character  
##                     Mean   :31.57   Mean   : 4.759                     
##                     3rd Qu.:38.23   3rd Qu.: 7.207                     
##                     Max.   :80.25   Max.   :28.000                     
##                     NA's   :12                                         
##       V5                 V6                 V7                  V8        
##  Length:690         Length:690         Length:690         Min.   : 0.000  
##  Class :character   Class :character   Class :character   1st Qu.: 0.165  
##  Mode  :character   Mode  :character   Mode  :character   Median : 1.000  
##                                                           Mean   : 2.223  
##                                                           3rd Qu.: 2.625  
##                                                           Max.   :28.500  
##                                                                           
##       V9                V10                 V11           V12           
##  Length:690         Length:690         Min.   : 0.0   Length:690        
##  Class :character   Class :character   1st Qu.: 0.0   Class :character  
##  Mode  :character   Mode  :character   Median : 0.0   Mode  :character  
##                                        Mean   : 2.4                     
##                                        3rd Qu.: 3.0                     
##                                        Max.   :67.0                     
##                                                                         
##      V13                 V14            V15               V16           
##  Length:690         Min.   :   0   Min.   :     0.0   Length:690        
##  Class :character   1st Qu.:  75   1st Qu.:     0.0   Class :character  
##  Mode  :character   Median : 160   Median :     5.0   Mode  :character  
##                     Mean   : 184   Mean   :  1017.4                     
##                     3rd Qu.: 276   3rd Qu.:   395.5                     
##                     Max.   :2000   Max.   :100000.0                     
##                     NA's   :13

3 Análisis Exploratorio de Datos (EDA)

La primera labor que debemos llevar a cabo al empezar a trabajar con un dataset desconocido es entender la información que este contiene. En esta fase, conocida como Análisis Exploratorio de Datos, llevaremos a cabo las siguientes tareas: - Etiquetado de los atributos: en vista de la ausencia de etiquetas interpretables para los atributos del dataset, en esta primera etapa hemos buscado entender el significado de cada uno de ellos y renombrarlos de manera más intuitiva.

3.1 Etiquetado de los atributos

En un primer momento, intentamos averiguar los significados de los campos de forma manual. Conseguimos deducir que el atributo ’V2 representa la edad del solicitante, percatándonos de que los decimales correspondían con múltiplos de 1/12 (por ejemplo, el valor 34.083 se corresponde con la edad de 34 años y 1 mes).

Otro campo que pudimos deducir fue el de Ingresos (V15), basándonos en la distribución que tomaban sus valores (y que veremos más adelante).

Finalmente, decidimos investigar en foros online para buscar el significado del resto de atributos. Ahí es cuando encontramos el siguiente proyecto de Kaggle.

Con esta información, renombramos las columnas:

colnames(credit)[colnames(credit) == "V1"] ="Sexo"
colnames(credit)[colnames(credit) == "V2"] ="Edad"
colnames(credit)[colnames(credit) == "V3"] ="Deuda"
colnames(credit)[colnames(credit) == "V4"] ="Estado_civil"
colnames(credit)[colnames(credit) == "V5"] ="Es_cliente"
colnames(credit)[colnames(credit) == "V6"] ="Nivel_educativo"
colnames(credit)[colnames(credit) == "V7"] ="Etnia"
colnames(credit)[colnames(credit) == "V8"] ="Anos_cotizados"
colnames(credit)[colnames(credit) == "V9"] ="Impago_previo"
colnames(credit)[colnames(credit) == "V10"] ="Trabaja"
colnames(credit)[colnames(credit) == "V11"] ="Calificacion_crediticia"
colnames(credit)[colnames(credit) == "V12"] ="Licencia_de_conducir"
colnames(credit)[colnames(credit) == "V13"] ="Ciudadano"
colnames(credit)[colnames(credit) == "V14"] ="Codigo_postal"
colnames(credit)[colnames(credit) == "V15"] ="Ingresos"
colnames(credit)[colnames(credit) == "V16"] ="Aprobado"

También distinguimos entre campos categóricos y numéricos. Los datos categóricos se deben tratar como el tipo factor. Algunos campos numéricos conviene visualizarlos en escala logarítmica.

campos = 1:15
campos_numericos = c(2, 3, 8, 11, 14, 15)
campos_log = c(8, 11, 15)
campos_no_log = setdiff(campos_numericos, campos_log)
campos_categoricos = setdiff(campos, campos_numericos)
#campos_categoricos = c("Sexo", "Estado_civil", "Es_cliente", "Nivel_educativo", "Etnia", "Impago_previo", "Trabaja", "Licenc", "Ciudadano", "Aprobado") #nolint
credit[campos_categoricos] <- lapply(credit[campos_categoricos], FUN = factor)
lapply(credit[campos_categoricos], FUN = levels)
## $Sexo
## [1] "a" "b"
## 
## $Estado_civil
## [1] "l" "u" "y"
## 
## $Es_cliente
## [1] "g"  "gg" "p" 
## 
## $Nivel_educativo
##  [1] "aa" "c"  "cc" "d"  "e"  "ff" "i"  "j"  "k"  "m"  "q"  "r"  "w"  "x" 
## 
## $Etnia
## [1] "bb" "dd" "ff" "h"  "j"  "n"  "o"  "v"  "z" 
## 
## $Impago_previo
## [1] "f" "t"
## 
## $Trabaja
## [1] "f" "t"
## 
## $Licencia_de_conducir
## [1] "f" "t"
## 
## $Ciudadano
## [1] "g" "p" "s"
#pasamos el campo aprobado también a factor y renombramos sus valores
credit <- credit %>%
  mutate(Aprobado = factor(Aprobado, levels = c("+", "-"), labels = c("Aprobado", "Rechazado")))

Añadimos los valores que faltan que pueden tomar algunas de las variables, en este caso a la variable estado civil le falta el valor “t”.

levels(credit$Estado_civil) <- c(levels(credit$Estado_civil), "t")

Para c da uno de los predictores categóricos, hemos rep – ¿Cu´antos ejemplos y variables predictorastiene el conjunto de datos? Distingue a las num´ericas de las categ´oricas. – Para cada predictor categ´orico, reporta, de la mejor forma posible, la distribuci´on de valores y com´entalo brevemente. – Para cada predictor num´erico, reporta m´ınimo, m´aximo, el primer cuartil, el tercer cuartil, media y mediana. Indica si la variable sigue o no una distribuci´on normal. – Responde, mediante una sola l´ınea de c´odigo en R, si el conjunto de datos tiene valores nulos y cu´antos nulos por columna. ∗ ¿Existe alguna columna digna de menci´on? ∗ Elabora una estrategia para el tratamiento de datos nulos y apl´ıcala en el resto de la pr´actica. – Representa las posibles relaciones de la variable de clase, de forma individual, con cada una de los predictores, haciendo uso de alg´un gr´afico con el mecanismo de las facetas (mostrar varios gr´aficos agrupados por alguna caracter´ıstica), y comenta los resultados. – Explora si el an´alisis de componentes principales es ´util, en este problema particular, como mecan- ismo de visualizaci´on e interpretaci´on de los datos para visualizar la separabilidad de las clases y comenta c´omo interpretar´ıas los dos primeros componentes principales. Busca la visualizaci´on m´as atractiva posible. Interpreta los datos de manera detallada a la luz de lo que te sugiere el mecanismo de visualizaci´on usado. – Recuerda analizar con cierta profundidad 3 predictores individualmente (an´alisis monovariable) y realizar al menos 1 an´alisis de forma multivariable. No es necesario analizarlos todos (para esta pr´actica analizar todos es excesivo), solo los tres m´as interesantes. Si analizas m´as de los pedidos deber´ıa estar MUY JUSTIFICADO

3.2 Análisis univariable

Para llevar a cabo el análisis monovariable, distinguiremos entre los atributos numéricos y categóricos, pues la forma de tratarlos es completamente distinta.

3.2.1 Atributos numéricos

Para los atributos numéricos, es interesante como punto de partida mostrar su valor mínimo, máximo, los cuartiles, la media y la mediana.

# Tabla con los atributos
atributosNum <- credit[,c(2,3,8,11,14,15)]

summary(atributosNum)
##       Edad           Deuda        Anos_cotizados   Calificacion_crediticia
##  Min.   :13.75   Min.   : 0.000   Min.   : 0.000   Min.   : 0.0           
##  1st Qu.:22.60   1st Qu.: 1.000   1st Qu.: 0.165   1st Qu.: 0.0           
##  Median :28.46   Median : 2.750   Median : 1.000   Median : 0.0           
##  Mean   :31.57   Mean   : 4.759   Mean   : 2.223   Mean   : 2.4           
##  3rd Qu.:38.23   3rd Qu.: 7.207   3rd Qu.: 2.625   3rd Qu.: 3.0           
##  Max.   :80.25   Max.   :28.000   Max.   :28.500   Max.   :67.0           
##  NA's   :12                                                               
##  Codigo_postal     Ingresos       
##  Min.   :   0   Min.   :     0.0  
##  1st Qu.:  75   1st Qu.:     0.0  
##  Median : 160   Median :     5.0  
##  Mean   : 184   Mean   :  1017.4  
##  3rd Qu.: 276   3rd Qu.:   395.5  
##  Max.   :2000   Max.   :100000.0  
##  NA's   :13

Vamos a mostrar ahora gráficamente estos atributos, junto con un histograma que nos sirve como primera toma de contacto con la distribución que siguen dichos atributos.

# Crear una lista para guardar los histogramas
plots <- list()

for (i in campos_numericos) {
  var <- names(credit)[i]
  var_data <- na.omit(credit[[var]])
# Saltar si la columna no tiene datos suficientes
  if (length(var_data) < 2) next
  df_temp <- data.frame(valor = var_data)
  
  p <- ggplot(df_temp, aes(x = valor)) +
    geom_histogram(bins = 30, fill = "skyblue", color = "black") +
    ggtitle(var) +
    theme_minimal()
  
  plots[[var]] <- p
}

grid.arrange(grobs = plots, ncol = 3)

Claramente hay variables como la de Ingresos o Calificacion_crediticia que pierden mucha información al representarse con el histograma. Utilizando escala logarítmica se puede distinguir mejor la población:

# Crear una lista para guardar los histogramas
plots <- list()

for (i in campos_log) {
    var <- names(credit)[i]
    var_data <- na.omit(credit[[var]])
    # Saltar si la columna no tiene datos suficientes o si hay valores no positivos
    var_data_log <- var_data[var_data > 0]
    if (length(var_data_log) < 2) next
    df_temp <- data.frame(valor = log(var_data_log))
    
    p <- ggplot(df_temp, aes(x = valor)) +
        geom_histogram(bins = 30, fill = "skyblue", color = "black") +
        ggtitle(paste0(var, " (log)")) +
        theme_minimal()
    
    plots[[var]] <- p
}

grid.arrange(grobs = plots, ncol = 3)

En el caso del atributo Calificacion_crediticia, vemos que hay una gran cantidad de muestras que tienen el valor 0 en este campo. Esto se puede deber a que el banco no conoce este dato o bien que la mayor parte de la gente acaba de abrirse la cuenta del banco y de momento no tiene una calificación crediticia positiva.

También vemos una cantidad elevado de personas con cero ingresos. Igual que en el caso anterior, seguramente se deba a desconocimiento del valor, pues no es frecuente que una cantidad tan elevada de la población tenga 0 ingresos.

Ahora queremos comprobar si alguno de los atributos sigue una distribución normal. Para ello utilizamos qqplot para comparar la función de distribución de nuestros atributos con la de la distribución normal:

library(ggplot2)
library(gridExtra)

# Lista para guardar los Q-Q plots
qqplots <- list()

for (i in campos_numericos) {
  var <- names(credit)[i]
  var_data <- na.omit(credit[[i]])

  # Saltar si hay pocos datos
  if (length(var_data) < 3) next

  df_temp <- data.frame(valor = var_data)

  # Q-Q plot usando stat_qq
  p <- ggplot(df_temp, aes(sample = valor)) +
    stat_qq() +
    stat_qq_line(color = "red") +
    ggtitle(paste(var)) +
    theme_minimal()

  qqplots[[var]] <- p
}

# Mostrar todos los Q-Q plots en una cuadrícula
grid.arrange(grobs = qqplots, ncol = 3)

Llegamos a la conclusión de que ninguno de los atributos sigue una distribución normal.

La que más se acerca sería la del código postal. Esto podría tener cierto sentido debido a que la mayor parte de los solicitantes vivirán cerca del banco en cuestión. Teniendo en cuenta que lugares cercanos suelen tener códigos postales próximos, tiene mucho sentido que cuanto más nos alejamos del banco (y por consecuente de su código postal), menos solicitantes habrá.

Siguiendo el razonamiento anterior, parece convenienente comprobar si las variables que anteriormente visualizamos en escala logarítmica, siguen una distribución lognormal. Para ello simplemente repetimos el procedimiento anterior con las variables tras aplicarle el logaritmo:

library(ggplot2)
library(gridExtra)

# Lista para guardar los Q-Q plots
qqplots <- list()

for (i in campos_log) {
  var <- names(credit)[i]
  var_data <- na.omit(credit[[var]])
# Saltar si la columna no tiene datos suficientes o si hay valores no positivos
  var_data_log <- var_data[var_data > 0]
  if (length(var_data_log) < 2) next
  df_temp <- data.frame(valor = log(var_data_log))


  # Q-Q plot usando stat_qq
  p <- ggplot(df_temp, aes(sample = valor)) +
    stat_qq() +
    stat_qq_line(color = "red") +
    ggtitle(paste(var)) +
    theme_minimal()

  qqplots[[var]] <- p
}

# Mostrar todos los Q-Q plots en una cuadrícula
grid.arrange(grobs = qqplots, ncol = 3)

Vemos que estas tres variables se ajustan mucho mejor a una distribución lognormal que a una normal.

Ahora vamos a centrarnos en tres predictores en concreto: ingresos, edad y deuda.

3.2.1.1 Análisis univariable del predictor Ingresos

library(ggplot2)

# Eliminamos NA por seguridad
ingresos <- na.omit(credit$Ingresos)

# Estadísticos básicos
summary(ingresos)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##      0.0      0.0      5.0   1017.4    395.5 100000.0
sd(ingresos)
## [1] 5210.103
# Histograma y Boxplot
p1 <- ggplot(data.frame(Ingresos = ingresos), aes(x = Ingresos)) +
  geom_histogram(bins = 30, fill = "steelblue", color = "black") +
  labs(title = "Histograma de Ingresos")

p2 <- ggplot(data.frame(Ingresos = ingresos), aes(y = Ingresos)) +
  geom_boxplot(fill = "orange") +
  labs(title = "Boxplot de Ingresos")

gridExtra::grid.arrange(p1, p2, ncol = 2)

La variable Ingresos presenta una distribución altamente asimétrica, con una gran concentración de valores bajos y una cola larga hacia la derecha. Esto se confirma con el histograma y el boxplot, donde se observan muchos valores en cero y varios outliers.

# Test de Shapiro-Wilk para normalidad
shapiro.test(ingresos)
## 
##  Shapiro-Wilk normality test
## 
## data:  ingresos
## W = 0.16985, p-value < 2.2e-16
# Log-transformación (se suman +1 para evitar log(0))
log_ingresos <- log(ingresos + 1)

# Test de Shapiro-Wilk sobre log(Ingresos)
shapiro.test(log_ingresos)
## 
##  Shapiro-Wilk normality test
## 
## data:  log_ingresos
## W = 0.82425, p-value < 2.2e-16

La prueba de Shapiro-Wilk sobre los ingresos originales da como resultado un p-valor muy bajo, indicando que no siguen una distribución normal. Tras aplicar una transformación logarítmica (log(Ingresos + 1)), la distribución mejora notablemente en forma, aunque el p-valor obtenido sigue siendo muy bajo, lo que nos confirma que tampoco sigue una distribución lognormal. El Q-Q plot también muestra una mejor alineación con la recta teórica tras la transformación.

# Q-Q plot para Ingresos y log(Ingresos)
par(mfrow = c(1,2))

qqnorm(ingresos, main = "Q-Q plot Ingresos")
qqline(ingresos, col = "red")

qqnorm(log_ingresos, main = "Q-Q plot log(Ingresos)")
qqline(log_ingresos, col = "red")

Nuestra siguiente intención es comprobar si este predictor sirve como una buena primera aproximación para distinguir entre personas a las que se le concedió el crédito y a las que no.

Para ello dividimos la población total entre aprobados y no aprobados y representamos con un boxplot cada una:

# Eliminar NA
df_ingresos <- credit[!is.na(credit$Ingresos) & !is.na(credit$Aprobado), ]

# Boxplot de Ingresos por Clase
ggplot(df_ingresos, aes(x = Aprobado, y = Ingresos, fill = Aprobado)) +
  geom_boxplot() +
  labs(title = "Distribución de Ingresos según Aprobado",
       x = "Aprobado (Crédito aprobado o no)",
       y = "Ingresos") +
  theme_minimal()

Al ser la mayoría de valores muy cercanos a cero perdemos mucha información al visualizar la gráfica. No obstante sí que nos damos cuenta de que a todos los outliers (personas con ingresos muy por encima de la media) se les concede el crédito, lo que nos hace pensar que puede ser un buen predictor.

Para visualizar mejor el boxplot, transformamos los datos a escala logaritmica:

  # También puedes ver la versión log-transformada (opcional)
df_ingresos$log_ingresos <- log(df_ingresos$Ingresos + 1)

ggplot(df_ingresos, aes(x = Aprobado, y = log_ingresos, fill = Aprobado)) +
  geom_boxplot() +
  labs(title = "Distribución log(Ingresos + 1) según Aprobado",
       x = "Aprobado",
       y = "log(Ingresos + 1)") +
  theme_minimal()

En este diagrama sí que se aprecia mucho mejor que a la gente que se le concedió el crédito tiene de media muchos más ingresos, confirmando así nuestra teoría.

Podemos mostrar también las funciones de densidad para cada una de las clases, en escala logarítmica para una mejor visualización:

ggplot(df_ingresos, aes(x = log_ingresos, fill = Aprobado)) +
  geom_density(alpha = 0.5) +
  labs(title = "Distribución de log(Ingresos+1) según Aprobado",
       x = "log(Ingresos+1)",
       y = "Densidad") +
  theme_minimal()

3.2.1.2 Análisis univariable del predictor Deuda

# Eliminamos NA
deuda <- na.omit(credit$Deuda)

# Estadísticos descriptivos
summary(deuda)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.000   1.000   2.750   4.759   7.207  28.000
sd(deuda)
## [1] 4.978163
# Histograma y Boxplot
p1 <- ggplot(data.frame(Deuda = deuda), aes(x = Deuda)) +
  geom_histogram(bins = 30, fill = "darkseagreen", color = "black") +
  labs(title = "Histograma de Deuda")

p2 <- ggplot(data.frame(Deuda = deuda), aes(y = Deuda)) +
  geom_boxplot(fill = "tomato") +
  labs(title = "Boxplot de Deuda")

gridExtra::grid.arrange(p1, p2, ncol = 2)

La variable Deuda presenta una distribución asimétrica, aunque menos extrema que Ingresos. El histograma muestra una acumulación de valores bajos y una cola hacia la derecha. El boxplot sugiere la presencia de varios valores atípicos por encima del tercer cuartil.

# Prueba de normalidad
shapiro.test(deuda)
## 
##  Shapiro-Wilk normality test
## 
## data:  deuda
## W = 0.83025, p-value < 2.2e-16
# Log-transformación si hay valores positivos
log_deuda <- log(deuda + 1)
shapiro.test(log_deuda)
## 
##  Shapiro-Wilk normality test
## 
## data:  log_deuda
## W = 0.96347, p-value = 4.345e-12

Ambas pruebas de Shapiro-Wilk indican que ni Deuda ni log(Deuda + 1) siguen una distribución normal. No obstante, el logaritmo ayuda a mejorar la simetría:

# Q-Q plots
par(mfrow = c(1,2))
qqnorm(deuda, main = "Q-Q plot Deuda")
qqline(deuda, col = "red")
qqnorm(log_deuda, main = "Q-Q plot log(Deuda)")
qqline(log_deuda, col = "red")

Queremos ahora ver si Deuda permite distinguir entre personas a las que se les concedió el crédito y a las que no:

df_deuda <- credit[!is.na(credit$Deuda) & !is.na(credit$Aprobado), ]
df_deuda$log_deuda <- log(df_deuda$Deuda + 1)

ggplot(df_deuda, aes(x = Aprobado, y = log_deuda, fill = Aprobado)) +
  geom_boxplot() +
  labs(title = "Distribución log(Deuda + 1) según Aprobado",
       x = "Aprobado",
       y = "log(Deuda + 1)") +
  theme_minimal()

El boxplot log-transformado permite ver que los casos aprobados tienden a tener mayor deuda, lo cual es contraintuitivo, pero puede estar relacionado con el hecho de que esas personas también tienen mayores ingresos y capacidad de pago. Este comportamiento debe analizarse junto con otros predictores.

ggplot(df_deuda, aes(x = Deuda, fill = Aprobado)) +
  geom_density(alpha = 0.5) +
  labs(title = "Distribución de Deuda según Aprobado",
       x = "Deuda",
       y = "Densidad") +
  theme_minimal()

Este gráfico muestra que, aunque las distribuciones se solapan, el grupo aprobado presenta una mayor densidad en valores altos de deuda, reforzando la idea de que tener deuda no impide ser aprobado, siempre que venga acompañada de otros factores como ingresos estables.

Podemos verlo en escala logarítmica de manera más evidente:

ggplot(df_deuda, aes(x = log_deuda, fill = Aprobado)) +
  geom_density(alpha = 0.5) +
  labs(title = "Distribución de Deuda según Aprobado",
       x = "Deuda",
       y = "Densidad") +
  theme_minimal()

3.2.2 Atributos categóricos

El estudio de campos categóricos es limitado en comparación con el de campos numéricos. Al no existir orden ni distancia entre valores de los atributos, perdemos la ayuda de la mayor parte de herramientas estadísticas. Sin embargo, esto no significa que no podamos extraer información útil de estos campos. Podemos comenzar nuetro análisis graficando un histograma de cada variable categórica.

summary(credit)
##    Sexo          Edad           Deuda        Estado_civil Es_cliente
##  a   :210   Min.   :13.75   Min.   : 0.000   l   :  2     g   :519  
##  b   :468   1st Qu.:22.60   1st Qu.: 1.000   u   :519     gg  :  2  
##  NA's: 12   Median :28.46   Median : 2.750   y   :163     p   :163  
##             Mean   :31.57   Mean   : 4.759   t   :  0     NA's:  6  
##             3rd Qu.:38.23   3rd Qu.: 7.207   NA's:  6               
##             Max.   :80.25   Max.   :28.000                          
##             NA's   :12                                              
##  Nivel_educativo     Etnia     Anos_cotizados   Impago_previo Trabaja
##  c      :137     v      :399   Min.   : 0.000   f:329         f:395  
##  q      : 78     h      :138   1st Qu.: 0.165   t:361         t:295  
##  w      : 64     bb     : 59   Median : 1.000                        
##  i      : 59     ff     : 57   Mean   : 2.223                        
##  aa     : 54     j      :  8   3rd Qu.: 2.625                        
##  (Other):289     (Other): 20   Max.   :28.500                        
##  NA's   :  9     NA's   :  9                                         
##  Calificacion_crediticia Licencia_de_conducir Ciudadano Codigo_postal 
##  Min.   : 0.0            f:374                g:625     Min.   :   0  
##  1st Qu.: 0.0            t:316                p:  8     1st Qu.:  75  
##  Median : 0.0                                 s: 57     Median : 160  
##  Mean   : 2.4                                           Mean   : 184  
##  3rd Qu.: 3.0                                           3rd Qu.: 276  
##  Max.   :67.0                                           Max.   :2000  
##                                                         NA's   :13    
##     Ingresos             Aprobado  
##  Min.   :     0.0   Aprobado :307  
##  1st Qu.:     0.0   Rechazado:383  
##  Median :     5.0                  
##  Mean   :  1017.4                  
##  3rd Qu.:   395.5                  
##  Max.   :100000.0                  
## 
categorical_vars <- names(credit)[campos_categoricos]

# Create a plotting function for categorical variables
plot_categorical <- function(var_name) {
  ggplot(credit, aes(x = .data[[var_name]])) +
    geom_bar(fill = "skyblue", color = "black") +
    labs(title = paste("Distribución de", var_name),
         x = var_name, y = "Cantidad") +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
}

categorical_plots <- map(categorical_vars, plot_categorical)

grid.arrange(grobs = categorical_plots, ncol = 3)

Nos gustaría también ver cómo los valores que toman estos campos afectan a la variable a predecir. Para ello podemos hacer uso de la función spineplot, que nos muestra para cada valor de una variable tanto su poblacion (ancho) como su tasa de aprobados (alto de la barra azul).

par(mfrow = c(3, 3))  #plots en cuadrícula 3x3

for (var in categorical_vars) {
  spineplot(x = credit[[var]], 
            y = credit$Aprobado,
            ylevels = c("Rechazado", "Aprobado"),
            xlab = var,
            ylab = "Resultado",
            main = paste("Aprobados por", var),
            col = c("#46cac1", "#ff8080"),
            border = c("#26958d", "#b13636"))
}

par(mfrow = c(1, 1))

Como punto de interés adicional, representemos también etnia frente a ciudadanía.

spineplot(credit$Etnia, credit$Ciudadano,
          xlab = "Etnia",
          ylab = "Es ciudadano",
          main = "Ciudadanía por Etnia",
          col = c("#aeaeae", "#848484", "#575757"))

Con los datos representados podemos realizar varias deducciones sobre el contexto del dataset y los significados de las etiquetas de algunos campos. Por ejemplo:

En el plot de etnia frente a ciudadanía, vemos que no existen grandes variaciones en las distribuciones de ciudadanía entre distintas etnias. Esto nos hace pensar que no estamos tratando con datos provenientes de un único país. Si fuese así, la etnia mayoritaria tendría probablemente un porcentaje de miembros ciudadanos muy superior al resto. Debemos suponer entonces, que aunque los datos provengan de un banco japonés, sus clientes están distribuidos por vairos países, y el campo “Es_ciudadano” hace referencia a la cuidadanía respecto al país en el que residen.

Trabaja: puesto que una persona con trabajo tiene mucha más facilidad para recibir aprobación por parte de un banco, resulta bastante claro que “t” y “f” se corresponden con tener y no tener
trabajo, respectivamente.

Impago previo: de manera bastante inmediata, el valor “f” de impago previo representa presencia de impagos por su terrible impacto en la tasa de aprobado (al contrario de lo que el nombre del campo pueda dar a entender). “t” significaría entonces ausencia de impagos. El valor de esta variable es un factor muy importante a la hora de decidir si se aprueba o no la solicitud. Tanto así que un predictor que utilice simplemente esta variable para clasificar, obtendría una tasa de aciertos de (306+284)/690 ~= 0.855, bastante cercano a lo que obtendremos con técnicas más avanzadas.

tab <- table(credit$Impago_previo, credit$Aprobado)
df_plot <- as.data.frame(tab)
names(df_plot) <- c("Impago_previo", "Aprobado", "Count")

ggplot(df_plot, aes(x = Impago_previo, y = Aprobado, fill = Count)) +
  geom_tile(color = "white") +
  geom_text(aes(label=Count), color = "black", size = 6) +
  scale_fill_gradient(low = "white", high = "steelblue") +
  labs(title = "Tabla de frecuencias de impago previo frente a aprobado",
       x = "Impago previo",
       y = "Aprobado") +
  theme_minimal(base_size = 20)

# Histogramas, summary(), boxplots, etc.

3.3 Análisis multivariable

Analizar todas las posibles agrupaciones de atributos sería una tarea inabarcable. Al contar con 16 de los mismos, existen un total de 216 = 65536 combinaciones de conjuntos. Es por ello que incluimos solo análisis de grupos de atributos que consideramos que pueden tener importancia.

Los objetivos del análisis multivariable es detectar patrones complejos no visibles en análisis univariantes. Nos puede servir posteriormente para reducir dimensiones o justificar otras decisiones de preprocesado.

3.3.1 Grid de variables numéricas

Utilizando un pairwise correlation plot con la función del paquete GGally vamos a visualizar las relaciones entre todos los pares de variables numéricas. Distinguimos entre créditos aprobados (azul) y denegados (rojo). No incluimos el código postal ya que no es un valor que mantenga un concepto de escala.

Dado que las variables Deuda, Anos_cotizados y Calificacion_crediticia presentan distribuciones muy sesgadas hacia valores bajos, aplicamos una transformación logarítmica. Así, se estabilizamos las varianzas y favorecemos el análisis de regresiones lineales.

# Función para mostrar R^2 en los paneles superiores
  cor_plus_r2_fun <- function(data, mapping, ...) {
    x <- eval_data_col(data, mapping$x)
    y <- eval_data_col(data, mapping$y)
    class_var <- data$Aprobado  # Asumimos que la clase es esta
    
    df <- data.frame(x = x, y = y, clase = class_var)
    df <- df[complete.cases(df), ]

    # Correlaciones
    r_total <- cor(df$x, df$y)
    r_plus  <- tryCatch(cor(df$x[df$clase == "Aprobado"], df$y[df$clase == "Aprobado"]), error = function(e) NA)
    r_minus <- tryCatch(cor(df$x[df$clase == "Rechazado"], df$y[df$clase == "Rechazado"]), error = function(e) NA)

    # R² global
    r2 <- tryCatch(summary(lm(y ~ x, data = df))$r.squared, error = function(e) NA)

    # Formato
    txt <- paste0(
      "Corr: ", sprintf("%.3f", r_total), "\n",
      "-: ", sprintf("%.3f", r_minus), "\n",
      "+: ", sprintf("%.3f", r_plus), "\n",
      "R²: ", sprintf("%.3f", r2)
    )

    ggplot(data = df, aes(x = x, y = y)) +
      annotate("text", x = median(df$x, na.rm = TRUE), y = median(df$y, na.rm = TRUE),
              label = txt, size = 3.5, hjust = 0.5) +
      theme_void()
  }

  # Subconjunto con las variables numéricas
  subset_credit <- credit[, c("Edad", "Deuda", "Anos_cotizados", "Calificacion_crediticia", "Ingresos", "Aprobado")]
  subset_credit$Deuda <- log10(subset_credit$Deuda + 1)
  subset_credit$Anos_cotizados <- log10(subset_credit$Anos_cotizados + 1)
  subset_credit$Ingresos <- log10(subset_credit$Ingresos + 1)
  subset_credit$Calificacion_crediticia <- log10(subset_credit$Calificacion_crediticia + 1)

  
  # Graficar con color por clase
  ggpairs(subset_credit,
          mapping = aes(color = Aprobado, alpha = 0.5),
          columns = 1:5,
          upper = list(continuous = cor_plus_r2_fun),
          lower = list(continuous = wrap("points", alpha = 0.4)),
          title = "Relaciones entre atributos numéricos")
## Warning: Removed 12 rows containing non-finite outside the scale range
## (`stat_density()`).
## Warning: Removed 12 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Removed 12 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Removed 12 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Removed 12 rows containing missing values or values outside the scale range
## (`geom_point()`).

Como puede observarse en la matriz de correlaciones, la diagonal corresponde a las distribuciones marginales que mostrábamos en el análisis monovariable. El triángulo inferior contiene los diagramas de dispersión para cada par de variables y clase (aprovado o denegado). Por último, el triángulo superior derecho contiene los coeficientes de correlación de Pearson, tanto de las distribuciones globales como las separadas por clase.

Las correlaciones son, en su mayoría, positivas y de pequeña magnitud. Tiene sentido, por ejemplo, que a mayor edad tenga una persona, más larga sea su vida laboral. Estas correlaciones son más intensas en la clase +. Esto sugiere que los casos aprobados siguen patrones más regulares. En el caso de la relación Edad-Anos_cotizados, podríamos interpretar que a la hora de la concesión de un crédito a una persona joven, se es mas laxo con una vida laboral más corta. No obstante, si nos fijamos en los gráficos de dispersión y en el R2, podemos concluir que los datos no son los idóneos para una regresión lineal.

Como separador, el par Edad-Deuda es el peor de todos, ya que las distribuciones de ambas clases se solapan demasiado. Todos los pares en los que aparece la calificación crediticia son buenas elecciones de separadores; no por ser un buen par, sino que como discutimos en el análisis monovariable, la calificación crediticia es en sí misma un buen separador. El par Deuda-Anos_cotizados es quizá el mejor separador bidimensional de la tabla. Se puede apreciar que un corte diagonal (recta y=1.5-x) se consigue una buena separación de los puntos rojos y azules.

subset_credit$Esfuerzo_financiero <- subset_credit$Deuda + subset_credit$Anos_cotizados  + abs(subset_credit$Ingresos-1)*0.375


ggplot(subset_credit, aes(x = Esfuerzo_financiero, fill = Aprobado)) +
  geom_density(alpha = 0.5) +
  theme_minimal() +
  labs(title = "Distribución del índice combinado log10(Deuda+1) + log10(Años cotizados+1) + |Igresos-1|*0.375" ,
       x = "Índice esfuerzo financiero", y = "Densidad")

Hemos llamado a la suma de ambos atributos (en escala logarítmica) mas el término |Ingresos-1|*0.375 Esfuerzo_financiero. La distribución marginal de este nuevo valor tiende a valores cercanos a cero en el caso de los créditos denegados y a una distribución simétrica centrada a la mitad del eje. A pesar del solapamiento, el patrón sugiere que este nuevo atributo podría tener valor como variable predictiva. Podría ser interesante como incorporación a los modelos de aprendizaje.

3.3.2 Relaciones del índice crediticio

La calificación crediticia es, a priori, el mejor clasificador monovariable. No obstante, sigue habiendo unos pocos casos en los que una calificación alta no implica la concesión del crédito. En esta subsección vamos a intentar encontrar cuáles son las variables que determinan la concesión del crédito en los casos de un índice crediticio alto.

Para empezar, filtramos el dataset para contener únicamente información sobre los créditos en los que el solicitante tiene un índice crediticio superior a 3.

  credit_alto <- subset(credit, Calificacion_crediticia > 3)
  summary(credit_alto)
##  Sexo        Edad           Deuda        Estado_civil Es_cliente
##  a:54   Min.   :16.08   Min.   : 0.000   l:  0        g :133    
##  b:97   1st Qu.:24.00   1st Qu.: 1.750   u:133        gg:  0    
##         Median :33.17   Median : 5.665   y: 18        p : 18    
##         Mean   :35.43   Mean   : 6.739   t:  0                  
##         3rd Qu.:43.16   3rd Qu.:10.312                          
##         Max.   :68.67   Max.   :28.000                          
##                                                                 
##  Nivel_educativo     Etnia    Anos_cotizados   Impago_previo Trabaja
##  c      :34      v      :80   Min.   : 0.000   f: 10         f:  0  
##  q      :26      h      :40   1st Qu.: 1.000   t:141         t:151  
##  cc     :14      bb     :15   Median : 2.375                        
##  w      :13      ff     : 9   Mean   : 4.010                        
##  x      :13      z      : 4   3rd Qu.: 5.250                        
##  i      : 9      dd     : 1   Max.   :28.500                        
##  (Other):42      (Other): 2                                         
##  Calificacion_crediticia Licencia_de_conducir Ciudadano Codigo_postal  
##  Min.   : 4.000          f:82                 g:149     Min.   :  0.0  
##  1st Qu.: 6.000          t:69                 p:  0     1st Qu.:  0.0  
##  Median : 8.000                               s:  2     Median :100.0  
##  Mean   : 9.344                                         Mean   :141.4  
##  3rd Qu.:11.000                                         3rd Qu.:220.0  
##  Max.   :67.000                                         Max.   :583.0  
##                                                         NA's   :2      
##     Ingresos            Aprobado  
##  Min.   :    0.0   Aprobado :135  
##  1st Qu.:    7.5   Rechazado: 16  
##  Median :  540.0                  
##  Mean   : 1919.4                  
##  3rd Qu.: 1802.5                  
##  Max.   :51100.0                  
## 

El resultado es una tabla de 151 entradas. El perfil medio de estas solicitudes, más allá de la calificación crediticia, se deferencia del perfil general en cuatro ámbitos:

  • Todos tienen un puestro de trabajo.

  • La deuda es mayor (6.739 vs 4.759 de media).

  • Más años cotizados (1 en el primer cuartil vs 0.165).

  • Más ingresos (540 vs 5 de media).

Si repetimos la matriz de correlaciones para este dataset:

# Función para mostrar R^2 en los paneles superiores
  cor_plus_r2_fun <- function(data, mapping, ...) {
    x <- eval_data_col(data, mapping$x)
    y <- eval_data_col(data, mapping$y)
    class_var <- data$Aprobado  # Asumimos que la clase es esta
    
    df <- data.frame(x = x, y = y, clase = class_var)
    df <- df[complete.cases(df), ]

    # Correlaciones
    r_total <- cor(df$x, df$y)
    r_plus  <- tryCatch(cor(df$x[df$clase == "Aprobado"], df$y[df$clase == "Aprobado"]), error = function(e) NA)
    r_minus <- tryCatch(cor(df$x[df$clase == "Rechazado"], df$y[df$clase == "Rechazado"]), error = function(e) NA)

    # R² global
    r2 <- tryCatch(summary(lm(y ~ x, data = df))$r.squared, error = function(e) NA)

    # Formato
    txt <- paste0(
      "Corr: ", sprintf("%.3f", r_total), "\n",
      "-: ", sprintf("%.3f", r_minus), "\n",
      "+: ", sprintf("%.3f", r_plus), "\n",
      "R²: ", sprintf("%.3f", r2)
    )

    ggplot(data = df, aes(x = x, y = y)) +
      annotate("text", x = median(df$x, na.rm = TRUE), y = median(df$y, na.rm = TRUE),
              label = txt, size = 3.5, hjust = 0.5) +
      theme_void()
  }

  # Subconjunto con las variables numéricas
  subset_credit <- credit_alto[, c("Edad", "Deuda", "Anos_cotizados", "Calificacion_crediticia", "Ingresos", "Aprobado")]
  subset_credit$Deuda <- log10(subset_credit$Deuda + 1)
  subset_credit$Anos_cotizados <- log10(subset_credit$Anos_cotizados + 1)
  subset_credit$Ingresos <- log10(subset_credit$Ingresos + 1)
  subset_credit$Calificacion_crediticia <- log10(subset_credit$Calificacion_crediticia + 1)

  
  # Graficar con color por clase
  ggpairs(subset_credit,
          mapping = aes(color = Aprobado, alpha = 0.5),
          columns = 1:5,
          upper = list(continuous = cor_plus_r2_fun),
          lower = list(continuous = wrap("points", alpha = 0.4)),
          title = "Relaciones entre atributos numéricos con alta Calificacion_crediticia")

los resultados obtenidos sugieren que los ingresos del solicitante son un factor clave en la concesión o no del crédito, resultando en una denegación aquellos perfiles con unos ingresos cercanos a 10 unidades. También destaca la diferencia entre una deuda alta y baja. De hecho, si utilizamos el Esfuerzo_financiero que hemos definido antes obtenemos el siguiente resultado:

subset_credit$Esfuerzo_financiero <- abs(subset_credit$Ingresos-1)*0.375 + subset_credit$Anos_cotizados + subset_credit$Deuda


ggplot(subset_credit, aes(x = Esfuerzo_financiero, fill = Aprobado)) +
  geom_density(alpha = 0.5) +
  theme_minimal() +
  labs(title = "Distribución log10(Deuda+1) + log10(Años cotizados+1) + |Ingresos-1|*0.375",
       x = "Índice esfuerzo financiero", y = "Densidad")

La separación es aún mejor que en el caso general. Podemos ver el rendimiento de este predictor para este subdataset:

  subset_credit$Prediccion_simple <- ifelse(subset_credit$Esfuerzo_financiero > 1.6, "Aprobado", "Rechazado")
  table(Real = subset_credit$Aprobado, Predicho = subset_credit$Prediccion_simple)
##            Predicho
## Real        Aprobado Rechazado
##   Aprobado       102        33
##   Rechazado        3        13
  library(ggplot2)
  library(dplyr)

  # Crear tabla de confusión en formato largo
  conf_mat <- table(Real = subset_credit$Aprobado, Predicho = subset_credit$Prediccion_simple)
  conf_df <- as.data.frame(conf_mat)

  # Añadir etiquetas
  conf_df <- conf_df %>%
    mutate(Tipo = case_when(
      Real == "Aprobado" & Predicho == "Aprobado" ~ "Verdadero Positivo",
      Real == "Rechazado" & Predicho == "Aprobado" ~ "Falso Positivo",
      Real == "Rechazado" & Predicho == "Rechazado" ~ "Verdadero Negativo",
      Real == "Aprobado" & Predicho == "Rechazado" ~ "Falso Negativo"
    ))

  
  # Gráfico
  ggplot(conf_df, aes(x = Predicho, y = Real, fill = Freq)) +
    geom_tile(color = "white") +
    geom_text(aes(label = paste0(Freq, "\n", Tipo)), size = 4) +
    scale_fill_gradient(low = "white", high = "steelblue") +
    labs(title = "Matriz de confusión para predictor basado en esfuerzo financiero",
        x = "Predicción", y = "Clase real", fill = "Frecuencia") +
    theme_minimal()

4 Preprocesado de datos

# Preprocesado 1 y preprocesado 2 para comparar más adelante
    # Cargar índices de train
    credit.trainIdx <- readRDS("credit.trainIdx")

    # Separación de conjuntos
    credit.Datos.Train <- credit[credit.trainIdx, ]
    credit.Datos.Test <- credit[-credit.trainIdx, ]

– ¿Hay predictores que no tengan utilidad y ser´ıan eliminables? ¿Por y en base a qu´e? – ¿Qu´e predictores habr´ıa que normalizar? ¿Por qu´e? ¿Cu´al ser´ıa la estrategia de normalizaci´on en cada caso? – ¿Podr´ıa ser interesante transformar alg´un atributo o grupos de atributos en uno nuevo? ¿Por qu´e?

Explica qué transformaciones se han aplicado: - Tratamiento de valores nulos - Normalización/escalado - Codificación de variables categóricas - Creación/eliminación de variables

4.1 Tratamiento de los valores nulos

En primer lugar, veamos si hay valores nulos y cuántos hay en cada columna:

# Una sola línea para contar nulos por columna
colSums(is.na(credit))
##                    Sexo                    Edad                   Deuda 
##                      12                      12                       0 
##            Estado_civil              Es_cliente         Nivel_educativo 
##                       6                       6                       9 
##                   Etnia          Anos_cotizados           Impago_previo 
##                       9                       0                       0 
##                 Trabaja Calificacion_crediticia    Licencia_de_conducir 
##                       0                       0                       0 
##               Ciudadano           Codigo_postal                Ingresos 
##                       0                      13                       0 
##                Aprobado 
##                       0

En una gráfica podemos verlos mejor:

# Visualización de los nulos con gráfico de barras
library(ggplot2)

nulos <- colSums(is.na(credit))
nulos_df <- data.frame(Variable = names(nulos), Nulos = nulos)
nulos_df <- nulos_df[nulos_df$Nulos > 0, ]  # Solo mostrar columnas con nulos

ggplot(nulos_df, aes(x = reorder(Variable, -Nulos), y = Nulos)) +
  geom_bar(stat = "identity", fill = "tomato") +
  labs(title = "Número de valores nulos por variable",
       x = "Variable", y = "Cantidad de NA") +
  theme_minimal() +
  coord_flip()

Vemos que solo 7 de los atributos tienen valores nulos y además la cantidad de estos es muy pequeña (cercano al 1% en todos los casos).

Es interesante saber cómo se distribuyen los valores nulos a lo largo de las muestras. Para ello vamos a ver la frecuencia del número de atributos faltantes. Ya trabajamos con los datos de Train, pues no debemos modificar los datos de Test a partir de ahora.

# Número de valores NA por fila
na_por_fila <- rowSums(is.na(credit.Datos.Train ))

# Observaciones con al menos un NA
con_na <- na_por_fila[na_por_fila > 0]
length(con_na)  # ¿Cuántas observaciones tienen al menos un NA?
## [1] 37
# Tabla resumen
table(con_na)
## con_na
##  1  2  3  5 
## 27  2  2  6
# Histograma: cuántos atributos faltan por observación (entre las que tienen algún NA)
library(ggplot2)

ggplot(data.frame(Faltantes = con_na), aes(x = Faltantes)) +
  geom_bar(fill = "steelblue") +
  labs(title = "Nº de atributos faltantes por observación",
       x = "Cantidad de NA",
       y = "Frecuencia") +
  theme_minimal()

Vemos que a la mayoría de muestras con valores NA solo les falta un valor. Sin embargo, también es importante destacar que hay 6 muestras a las cuales les faltan 5 valores.

Como estrategia de limpieza, se han eliminado las observaciones del conjunto de entrenamiento que contenían más de 4 valores nulos. Esta decisión se justifica por el hecho de que esas observaciones presentan información muy incompleta, y su imputación generaría más ruido que beneficio. El resto de las observaciones, con pocos valores faltantes, se conservarán y se decidirá si se inputan o no posteriormente.

# Eliminar observaciones con más de 4 atributos faltantes
credit.Datos.Train <- credit.Datos.Train[na_por_fila <= 4, ]

# Verificamos tamaño tras limpieza
nrow(credit.Datos.Train)
## [1] 547

Los valores nulos deben tratarse adecuadamente antes de entrenar modelos de aprendizaje automático. Las posibles estrategias incluyen:

  • Eliminación de filas: útil si el número de nulos es pequeño y su eliminación no reduce significativamente el tamaño del dataset.

  • Imputación por la media/mediana/moda: recomendable para variables numéricas si el porcentaje de nulos es moderado.

  • Imputación por valor más frecuente: útil en variables categóricas con pocos niveles.

  • Modelado de imputación: usar modelos predictivos (por ejemplo mice, missForest) para estimar los valores faltantes, aunque es más costoso computacionalmente.

En nuestro caso, es probable que la mejor opción sea simplemente eliminar las filas con datos faltantes, pues como hemos visto en la gráfica anterior, hay muy pocos valores nulos en comparación con el número total de muestras que disponemos.

4.1.1 Creación y análisis del predictor DeudaRelativa

Hemos pensado en crear la variable DeudaRelativa, que creemos que tiene más sentido que la variable Deuda, pues no es tan importante la deuda que tengas, sino cómo de grande es esta deuda comparada con tus ingresos.

# Crear la nueva variable
credit.Datos.Train$DeudaRelativa <- log(1 + credit.Datos.Train$Deuda / (credit.Datos.Train$Ingresos + 1))
credit.Datos.Test$DeudaRelativa <- log(1 + credit.Datos.Test$Deuda / (credit.Datos.Test$Ingresos + 1))
# Boxplot de DeudaRelativa según Aprobado
ggplot(credit.Datos.Train, aes(x = Aprobado, y = DeudaRelativa, fill = Aprobado)) +
  geom_boxplot() +
  labs(title = "Relación Deuda / Ingresos según Aprobación",
       x = "Aprobado",
       y = "DeudaRelativa") +
  theme_minimal()

# Gráfico de densidad por clase
ggplot(credit.Datos.Train, aes(x = DeudaRelativa, fill = Aprobado)) +
  geom_density(alpha = 0.5) +
  labs(title = "Distribución de DeudaRelativa por clase",
       x = "DeudaRelativa",
       y = "Densidad") +
  theme_minimal()

Tanto el boxplot como la función de densidad muestran que los casos aprobados suelen tener una deuda relativa menor, lo que es coherente con que los prestamistas aprueben más fácilmente a quienes tienen un endeudamiento controlado respecto a sus ingresos.

Durante el entrenamiento de los modelos comprobaremos si la inclusión de esta variable reporta alguna mejoría respecto a usar únicamente la deuda bruta.

4.2 Análisis de Componentes Principales (PCA)

PCA es una técnica de análisis de datos utilizada para representar un conjunto de datos multidimensionales mediante un conjunto más reducido de variables generadas como combinaciones lineales de sus componentes. Esto se consigue buscando la proyección según la cual los datos presentan una mayor varianza. En nuestro caso solo 6 de los campos son numéricos. Los campos que antes representábamos en escala logarítmica se pasan también en esta escala. No utilizamos el código postal ya que no es un dato continuo.

    subset_train <- na.omit(credit.Datos.Train)
    subset_train$Deuda <- log10(subset_train$Deuda + 1)
    subset_train$Anos_cotizados <- log10(subset_train$Anos_cotizados + 1)
    subset_train$Calificacion_crediticia <- log10(subset_train$Calificacion_crediticia + 1)
    subset_train$Ingresos <- log10(subset_train$Ingresos + 1)
    pca <- subset_train[c("Edad", "Deuda", "Anos_cotizados", "Calificacion_crediticia", "Ingresos")]
    pca
##      Edad      Deuda Anos_cotizados Calificacion_crediticia  Ingresos
## 1   30.83 0.00000000     0.35218252               0.3010300 0.0000000
## 4   27.83 0.40483372     0.67669361               0.7781513 0.6020600
## 5   20.17 0.82118588     0.43296929               0.0000000 0.0000000
## 6   32.08 0.69897000     0.54406804               0.0000000 0.0000000
## 7   33.17 0.30963017     0.87506126               0.0000000 4.4953500
## 8   22.92 1.09985322     0.01703334               0.0000000 3.1303338
## 9   54.42 0.17609126     0.69548168               0.0000000 2.4983106
## 10  42.50 0.77195475     0.61961501               0.0000000 3.1592663
## 13  38.25 0.84509804     0.30103000               0.0000000 0.0000000
## 16  36.67 0.73359846     0.09691001               1.0413927 0.0000000
## 17  28.25 0.27300127     0.29225607               0.6020600 0.0000000
## 19  21.83 0.09691001     0.22141424               0.0000000 0.0000000
## 20  19.17 0.98159212     0.24303805               0.9030900 0.0000000
## 23  47.75 0.95424251     0.94816836               0.8450980 3.1007151
## 24  27.42 1.19033170     0.61119206               0.3010300 1.0791812
## 26  15.83 0.20002927     0.39794001               0.4771213 0.0000000
## 27  47.00 1.14612804     0.78993308               1.0000000 0.0000000
## 28  56.58 1.29003461     1.20411998               1.2552725 0.0000000
## 29  57.42 0.97772361     0.90308999               0.6020600 0.0000000
## 30  42.08 0.30963017     0.77815125               0.8450980 4.0000434
## 33  49.50 0.93374030     0.93374030               1.2041200 3.6990569
## 34  36.75 0.78710609     0.77815125               0.0000000 3.6021686
## 36  27.83 0.39794001     0.47712125               1.0791812 1.5563025
## 37  27.25 0.41246055     0.45255306               1.1139434 2.8536982
## 38  23.00 1.10551018     0.17609126               0.4771213 2.7419391
## 39  27.75 0.20002927     0.09691001               0.4771213 2.6998377
## 41  34.17 1.00732095     0.74036269               1.1139434 2.3463530
## 44  39.58 1.17362326     0.98340074               0.8450980 0.0000000
## 45  56.42 1.46239800     1.46982202               1.6127839 1.2041200
## 46  54.33 0.88930170     0.55930801               1.0791812 2.4548449
## 47  41.00 0.48287358     0.05115252               1.3802112 3.0923697
## 48  31.92 0.73719264     0.84757266               0.6020600 2.4785665
## 49  41.50 0.40483372     0.65321251               0.0000000 0.0000000
## 50  23.92 0.22141424     0.06632593               0.0000000 0.0000000
## 51  25.75 0.17609126     0.27300127               0.0000000 0.0000000
## 52  26.00 0.30103000     0.43933269               0.0000000 0.0000000
## 53  37.42 0.48287358     0.01703334               0.0000000 3.7635029
## 54  34.92 0.54406804     0.00000000               0.0000000 2.3031961
## 55  34.25 0.60205999     0.92505412               0.0000000 0.0000000
## 56  23.33 1.10123139     0.26363607               0.0000000 2.4785665
## 57  23.17 0.00000000     0.03542974               0.0000000 0.0000000
## 58  44.33 0.17609126     0.77815125               0.0000000 0.0000000
## 59  35.17 0.74036269     0.82930377               0.0000000 0.0000000
## 61  56.75 1.12221588     0.35218252               0.6989700 0.0000000
## 62  31.67 1.23464381     0.60205999               1.0000000 2.8639174
## 63  23.42 0.25285303     0.39794001               0.4771213 2.6031444
## 64  20.42 0.26363607     0.41246055               0.3010300 0.0000000
## 65  26.67 0.72015930     0.72345567               0.3010300 0.0000000
## 66  34.17 0.40483372     0.40483372               0.3010300 4.6989787
## 67  36.00 0.30103000     0.47712125               1.0791812 2.6599162
## 70  35.17 1.41705630     0.41912931               0.3010300 2.6998377
## 71  32.33 0.92941893     0.41246055               0.0000000 0.0000000
## 74  44.25 0.17609126     1.07003787               0.0000000 0.0000000
## 75  44.83 0.90308999     0.41912931               0.0000000 0.4771213
## 76  20.67 0.79865065     0.13830270               0.3010300 0.0000000
## 77  34.08 0.87506126     0.05115252               0.0000000 0.0000000
## 79  21.67 0.33545790     0.54406804               0.3010300 1.3222193
## 80  21.50 1.03140846     0.09691001               0.0000000 0.0000000
## 81  49.58 1.30103000     0.00000000               0.3010300 0.0000000
## 82  27.67 0.39794001     0.47712125               0.0000000 0.0000000
## 83  39.83 0.17609126     0.09691001               0.0000000 0.0000000
## 85  27.25 0.21085337     0.16286299               0.0000000 0.0000000
## 88  25.67 0.50650503     0.69897000               0.0000000 0.0000000
## 89  34.00 0.74036269     0.30103000               0.0000000 0.0000000
## 90  49.00 0.39794001     0.00000000               0.0000000 1.4471580
## 92  31.42 1.21748394     0.17609126               0.0000000 0.0000000
## 95  28.75 0.39794001     0.39794001               0.0000000 2.3541084
## 96  28.58 0.65705585     0.17609126               0.0000000 0.0000000
## 97  23.00 0.21085337     0.05115252               0.0000000 0.3010300
## 100 28.50 0.30103000     0.30103000               0.4771213 2.6998377
## 101 37.50 0.43933269     0.09691001               0.0000000 2.6031444
## 102 35.25 1.24303805     0.69897000               0.0000000 0.0000000
## 104 25.00 1.11394335     0.51188336               0.4771213 0.7781513
## 106 54.83 1.21748394     0.00000000               1.3222193 2.1172713
## 107 28.75 0.33545790     0.17609126               0.0000000 0.0000000
## 108 25.00 1.07918125     0.74036269               0.0000000 0.0000000
## 110 19.75 0.24303805     0.25406445               0.7781513 0.7781513
## 111 29.17 0.65321251     0.65321251               0.6020600 0.0000000
## 112 24.50 0.30963017     0.17609126               0.6020600 2.1702617
## 116 25.42 0.32735893     0.35983548               0.4771213 0.0000000
## 117 37.75 0.90308999     1.09691001               0.9030900 0.7781513
## 118 52.50 0.87506126     0.86272753               1.2041200 4.0493343
## 120 20.75 1.05442152     0.12548127               0.3010300 1.7075702
## 121 39.92 0.85793526     0.01703334               0.3010300 2.4785665
## 122 25.67 1.13033377     0.34439227               1.8325089 2.4132998
## 123 24.75 1.13033377     0.39794001               1.1139434 2.7543483
## 124 44.17 0.88451216     0.92298482               0.6020600 0.0000000
## 126 34.92 0.77815125     0.92941893               0.8450980 3.0004341
## 128 22.75 1.07918125     0.54406804               0.9030900 2.9084850
## 129 34.42 0.72015930     0.62838893               0.4771213 2.7860412
## 133 47.42 0.95424251     0.87506126               0.8450980 4.7084294
## 134 36.25 0.77815125     0.54406804               0.8450980 2.5658478
## 135 32.67 0.81291336     0.81291336               1.1139434 3.0004341
## 136 48.58 0.87506126     0.84509804               0.0000000 0.0000000
## 137 39.92 0.18752072     0.17609126               0.6020600 3.0004341
## 138 33.58 0.57403127     0.72015930               0.8450980 0.0000000
## 139 18.83 1.02118930     0.41912931               0.8450980 2.7788745
## 140 26.92 1.16136800     0.77815125               0.4771213 3.6990569
## 141 31.25 0.67669361     0.21085337               1.0000000 0.0000000
## 142 56.50 1.23044892     0.00000000               1.2041200 2.3944517
## 144 22.33 1.07918125     0.47712125               0.3010300 2.4456042
## 145 27.25 0.42569721     0.78426058               1.0000000 2.9180303
## 146 32.83 0.54406804     0.57403127               0.8450980 3.3165993
## 147 23.25 0.39794001     0.52827378               0.6020600 2.7656686
## 149 30.50 0.87506126     0.69897000               0.9030900 3.4865722
## 150 52.83 1.20411998     0.81291336               1.1760913 3.3426200
## 151 46.67 0.16435286     0.15075644               1.0791812 0.8450980
## 152 58.33 1.04139269     0.69897000               1.1760913 3.2049335
## 153 37.33 0.87506126     0.72015930               1.1139434 0.0000000
## 154 23.08 0.54406804     0.31910606               1.0791812 3.3394514
## 156 21.67 1.09691001     0.00000000               1.0791812 0.0000000
## 157 28.50 0.60638137     0.54900326               0.3010300 0.0000000
## 158 68.67 1.20411998     0.00000000               1.1760913 3.5285311
## 160 34.08 0.03342376     0.01703334               0.3010300 3.3012471
## 161 27.67 0.47712125     0.30103000               0.6989700 3.8776592
## 163 25.08 0.43296929     0.42569721               0.3010300 1.3222193
## 164 32.00 0.43933269     0.01703334               0.0000000 0.0000000
## 165 60.58 1.24303805     1.07918125               0.0000000 4.0237462
## 166 40.83 1.04139269     0.43933269               0.0000000 2.9232440
## 167 19.33 1.02118930     0.30103000               0.0000000 2.6031444
## 169 36.67 0.62838893     1.00000000               0.0000000 2.8061800
## 170 37.50 0.32735893     0.39794001               0.0000000 0.0000000
## 171 25.08 0.54900326     0.09691001               0.0000000 0.0000000
## 174 49.83 1.16390643     0.97772361               0.0000000 0.0000000
## 175 22.67 1.06069784     0.36828688               0.0000000 0.0000000
## 176 27.00 0.39794001     0.13830270               0.0000000 3.0277572
## 178 26.08 0.98520186     0.38291714               0.0000000 2.1789769
## 179 18.42 1.01072387     0.34439227               0.6989700 2.7331973
## 180 20.17 0.96236934     0.47129171               1.1760913 2.2013971
## 181 47.67 0.11058971     1.20411998               1.3222193 4.1761202
## 182 21.25 0.52309584     0.17609126               0.6989700 0.0000000
## 183 20.67 0.60205999     0.06632593               0.6020600 0.8450980
## 184 57.08 1.31175386     0.81291336               0.9030900 3.4772660
## 185 22.42 0.82380015     0.55448916               0.9030900 3.5129511
## 186 48.75 0.97772361     1.13033377               1.0000000 3.2190603
## 187 40.00 0.87506126     0.65321251               0.3010300 2.6998377
## 188 40.58 0.77815125     0.77815125               0.9030900 3.4865722
## 189 28.67 0.30963017     0.54406804               0.7781513 3.1556396
## 190 33.08 0.75012253     0.41912931               0.4771213 0.0000000
## 191 21.33 1.06069784     0.60205999               0.0000000 0.0000000
## 192 42.00 0.08098705     0.78710609               0.0000000 0.0000000
## 193 41.75 0.29225607     0.54406804               0.0000000 2.7788745
## 194 22.67 0.41246055     0.61119206               0.8450980 0.0000000
## 195 34.50 0.70243054     0.97772361               0.9030900 0.0000000
## 196 28.25 0.78103694     0.39794001               0.9542425 0.9030900
## 197 33.17 0.61961501     0.61961501               0.6020600 0.0000000
## 198 48.17 0.93575910     1.21748394               1.1139434 2.8981765
## 199 27.58 0.48287358     0.47712125               0.6020600 2.7489629
## 200 22.58 1.04296907     0.01703334               1.0000000 2.5987905
## 201 24.08 0.17609126     0.35218252               0.3010300 2.8318698
## 202 41.33 0.30103000     0.51188336               0.0000000 2.4785665
## 204 20.75 1.05115252     0.23299611               0.4771213 0.0000000
## 208 28.67 1.01431048     0.82380015               0.8450980 2.2278867
## 209 35.17 0.54406804     0.74036269               0.9030900 3.1041456
## 210 39.50 0.72015930     0.87506126               1.2304489 3.0831441
## 211 39.33 0.83727270     1.04139269               1.1760913 0.0000000
## 212 24.33 0.88223985     0.81291336               0.0000000 0.0000000
## 213 60.08 1.19033170     1.27875360               1.2041200 3.0004341
## 214 23.08 1.09691001     0.65321251               1.0000000 2.8709888
## 215 26.67 0.56937391     0.79588002               0.3010300 0.0000000
## 217 41.17 0.70243054     0.90308999               0.9542425 0.0000000
## 218 55.92 1.09691001     0.77815125               0.7781513 3.9470414
## 219 53.92 1.02632894     0.98520186               0.7781513 0.0000000
## 221 50.08 1.13161866     0.51719590               0.6020600 0.0000000
## 222 65.42 1.07918125     1.32221929               0.9030900 0.0000000
## 223 17.58 1.00000000     0.37566361               0.0000000 0.0000000
## 224 18.83 1.02284061     0.03542974               0.0000000 0.0000000
## 225 37.75 0.81291336     0.05115252               0.0000000 0.0000000
## 226 23.25 0.69897000     0.09691001               0.0000000 0.0000000
## 227 18.08 0.81291336     0.17609126               0.0000000 0.0000000
## 228 22.50 0.97589114     0.53907610               0.0000000 0.0000000
## 230 22.08 1.07918125     0.22141424               0.0000000 0.0000000
## 231 25.17 0.65321251     0.21085337               0.9030900 3.8488047
## 232 47.42 0.60205999     1.17245697               0.4771213 3.2317244
## 234 27.67 1.16879202     0.82930377               0.0000000 2.6998377
## 235 58.42 1.34242268     1.04139269               1.1461280 3.8261396
## 236 20.67 0.45255306     0.48925517               0.7781513 3.3986343
## 237 26.17 0.09691001     0.00000000               0.0000000 0.0000000
## 241 20.50 1.04139269     0.54406804               0.0000000 0.0000000
## 242 48.25 1.41639084     0.43933269               0.6020600 1.1760913
## 243 28.33 0.77815125     1.07918125               0.0000000 0.0000000
## 245 18.50 0.47712125     0.39794001               0.4771213 2.4785665
## 246 33.17 0.60638137     0.48287358               0.3010300 4.2559475
## 247 45.00 0.97772361     1.17609126               0.3010300 3.3012471
## 248 19.67 0.08278537     0.11058971               1.0791812 2.0000000
## 250 21.83 1.07918125     0.11058971               0.8450980 0.0000000
## 251 40.25 1.35218252     1.32221929               1.0791812 3.0795430
## 253 17.83 1.07918125     0.30103000               1.0791812 3.4772660
## 257 20.00 1.08080680     0.47712125               0.0000000 0.0000000
## 258 20.00 0.00000000     0.17609126               0.0000000 0.0000000
## 259 20.75 1.02284061     0.01703334               0.0000000 3.0004341
## 261 32.75 0.52309584     0.82930377               0.0000000 0.0000000
## 262 52.17 0.00000000     0.00000000               0.0000000 0.0000000
## 263 48.17 0.36828688     0.12548127               0.0000000 2.0827854
## 264 20.42 1.06069784     0.00000000               0.0000000 1.5185139
## 265 50.75 0.20002927     0.00000000               0.0000000 0.0000000
## 266 17.08 0.03542974     0.01703334               0.0000000 2.8591383
## 267 18.33 0.34439227     0.00000000               0.0000000 0.0000000
## 268 32.00 0.84509804     0.35218252               0.0000000 0.0000000
## 269 59.67 0.40483372     0.05115252               0.0000000 0.0000000
## 270 18.00 0.06632593     0.08278537               0.0000000 1.6127839
## 274 38.25 1.04630002     0.05115252               0.0000000 0.0000000
## 275 30.67 0.54406804     0.51188336               0.0000000 0.0000000
## 276 18.58 0.82672252     0.18752072               0.0000000 0.0000000
## 277 19.17 0.80719666     0.11058971               0.0000000 2.6857417
## 278 18.17 1.04139269     0.06632593               0.0000000 0.0000000
## 281 21.17 0.27300127     0.09691001               0.0000000 2.3117539
## 282 23.92 0.20002927     0.05115252               0.0000000 0.3010300
## 283 17.67 0.73719264     0.09691001               0.0000000 0.0000000
## 284 16.50 0.35218252     0.09691001               0.3010300 1.9956352
## 285 23.25 1.13433651     0.05115252               0.4771213 3.7445277
## 288 29.50 0.19865709     0.11058971               0.3010300 3.4477780
## 290 21.75 0.43933269     0.00000000               0.0000000 0.0000000
## 291 23.00 0.24303805     0.17609126               0.0000000 0.0000000
## 292 18.25 1.04139269     0.30103000               0.3010300 0.3010300
## 295 16.08 0.12548127     0.00000000               0.3010300 2.1038037
## 296 31.92 0.61542395     0.60638137               0.4771213 0.6989700
## 297 69.17 1.00000000     0.69897000               0.3010300 0.8450980
## 298 32.92 0.54406804     0.43933269               0.4771213 0.0000000
## 299 16.33 0.57403127     0.22141424               0.3010300 1.3424227
## 300 22.17 1.11809931     0.63698910               0.4771213 2.2405492
## 301 57.58 0.47712125     0.87506126               0.3010300 1.0413927
## 302 18.25 0.06632593     0.09691001               0.0000000 0.0000000
## 303 23.42 0.30103000     0.17609126               0.0000000 0.0000000
## 304 15.92 0.58827171     0.03542974               0.0000000 0.0000000
## 305 24.75 1.16628207     0.39794001               0.0000000 0.3010300
## 306 48.75 1.43671908     0.00000000               0.0000000 0.0000000
## 307 23.50 0.57403127     0.74036269               0.0000000 1.4149733
## 308 18.58 1.05269394     0.15075644               0.0000000 0.0000000
## 309 27.75 0.35983548     0.09691001               0.0000000 0.0000000
## 310 31.75 0.60205999     0.00000000               0.0000000 1.3222193
## 311 24.83 0.74036269     0.30103000               0.0000000 0.8450980
## 312 19.00 0.43933269     0.52309584               0.0000000 0.8450980
## 314 18.58 1.04139269     0.15075644               0.0000000 1.6334685
## 315 16.25 0.00000000     0.09691001               0.0000000 0.0000000
## 316 23.00 0.24303805     0.17609126               0.0000000 0.0000000
## 317 21.17 0.09691001     0.09691001               0.0000000 2.3117539
## 318 17.50 1.36172784     0.00000000               0.0000000 5.0000043
## 319 19.17 0.00000000     0.00000000               0.0000000 0.3010300
## 320 36.75 0.05115252     0.39794001               0.0000000 2.0569049
## 321 21.25 0.39794001     0.39794001               0.0000000 0.9542425
## 322 18.08 0.13830270     1.04139269               0.0000000 0.0000000
## 323 33.67 0.13830270     0.13830270               0.0000000 1.6532125
## 325 33.67 0.35218252     0.33545790               0.0000000 0.0000000
## 327 30.17 0.31910606     0.01703334               0.0000000 2.2552725
## 329 34.83 0.54406804     0.60205999               0.0000000 0.0000000
## 332 33.25 0.54406804     0.54406804               0.0000000 0.4771213
## 333 34.08 0.54406804     0.30103000               0.0000000 1.2304489
## 336 27.67 0.24303805     0.06632593               0.0000000 2.4014005
## 337 47.33 0.87506126     0.30103000               0.0000000 2.3598355
## 338 34.83 0.35218252     0.17609126               0.0000000 0.0000000
## 339 33.25 0.60205999     0.47712125               0.0000000 0.0000000
## 340 28.00 0.60205999     0.24303805               0.0000000 1.8325089
## 341 39.08 0.69897000     0.60205999               0.0000000 0.0000000
## 342 42.75 0.70629096     0.01703334               0.0000000 2.0043214
## 343 26.92 0.51188336     0.17609126               0.0000000 3.6021686
## 344 33.75 0.57403127     0.00000000               0.0000000 0.0000000
## 345 38.92 0.43933269     0.17609126               0.0000000 0.4771213
## 346 62.75 0.90308999     0.00000000               0.0000000 1.1139434
## 348 26.75 0.74036269     0.54406804               0.0000000 3.0831441
## 349 63.33 0.18752072     0.20002927               0.6020600 0.0000000
## 350 27.83 0.39794001     0.51188336               0.3010300 0.6020600
## 352 22.17 0.20002927     0.00000000               0.0000000 0.0000000
## 353 22.50 1.09691001     0.39794001               0.0000000 3.6021686
## 354 30.75 0.41246055     0.20002927               0.0000000 0.0000000
## 355 36.67 0.47712125     0.09691001               0.0000000 0.0000000
## 356 16.00 0.06632593     0.30103000               0.4771213 0.3010300
## 357 41.17 0.36828688     0.06632593               0.0000000 0.0000000
## 358 19.50 0.06632593     0.01703334               0.0000000 0.0000000
## 359 32.42 0.60205999     0.06632593               0.0000000 0.0000000
## 361 30.25 0.81291336     0.81291336               0.0000000 0.0000000
## 363 26.83 0.18752072     0.00000000               0.0000000 0.0000000
## 364 16.92 0.12548127     0.11058971               0.0000000 0.0000000
## 365 24.42 0.47712125     0.06632593               0.4771213 3.1142773
## 367 22.75 0.85521619     0.06632593               0.0000000 3.0004341
## 369 23.58 1.09691001     0.60205999               0.0000000 1.2304489
## 370 21.42 0.24303805     0.24303805               0.0000000 0.4771213
## 371 33.00 0.54406804     0.90308999               0.0000000 0.0000000
## 372 26.33 1.14612804     0.00000000               0.0000000 3.0457141
## 374 26.25 0.40483372     0.05115252               0.0000000 0.0000000
## 376 20.83 0.17609126     0.30103000               0.0000000 0.0000000
## 377 28.67 1.19033170     0.05115252               0.0000000 2.4578819
## 378 20.67 0.26363607     0.47712125               0.0000000 0.0000000
## 379 34.42 0.36828688     0.05115252               0.0000000 3.6533090
## 380 33.58 0.09691001     0.69897000               0.0000000 0.0000000
## 381 43.17 0.77815125     0.51188336               0.0000000 0.0000000
## 382 22.67 0.90308999     0.06632593               0.0000000 0.0000000
## 383 24.33 0.54406804     0.74036269               0.0000000 2.6599162
## 385 22.08 1.09551804     0.41246055               0.0000000 3.0838608
## 387 22.58 0.39794001     0.18752072               0.0000000 1.8325089
## 388 21.17 0.00000000     0.17609126               0.0000000 0.0000000
## 389 26.67 1.19270681     0.00000000               0.0000000 0.0000000
## 391 15.17 0.90308999     0.30103000               0.0000000 0.0000000
## 392 39.92 0.77815125     0.08278537               0.0000000 0.0000000
## 393 27.42 1.13033377     0.09691001               0.0000000 0.0000000
## 394 24.75 0.18752072     0.30103000               0.0000000 0.3010300
## 395 41.17 0.35218252     0.09691001               0.0000000 2.2922561
## 396 33.08 0.41912931     0.18752072               0.0000000 0.0000000
## 397 29.83 0.48287358     0.01703334               0.0000000 0.3010300
## 398 23.58 0.20002927     0.05115252               0.0000000 1.9444827
## 399 26.17 1.13033377     0.35218252               0.0000000 1.2552725
## 400 31.00 0.48925517     0.03542974               0.0000000 0.0000000
## 401 20.75 0.78426058     0.11058971               0.0000000 2.2671717
## 402 28.92 0.13830270     0.11058971               0.0000000 2.1492191
## 403 51.92 0.87506126     0.61119206               0.0000000 0.0000000
## 404 22.67 0.12548127     0.24303805               0.0000000 0.0000000
## 405 34.00 0.78426058     0.31910606               0.0000000 0.0000000
## 406 69.50 0.84509804     0.00000000               0.0000000 0.0000000
## 408 19.58 0.22141424     0.30103000               0.3010300 0.4771213
## 409 16.00 0.61542395     0.03542974               0.3010300 0.8450980
## 410 17.08 0.09691001     0.12548127               0.6989700 0.9542425
## 411 31.25 0.58376537     0.00000000               0.7781513 2.1673173
## 413 22.67 0.25285303     0.03542974               0.0000000 0.0000000
## 415 22.25 0.16435286     0.05115252               0.0000000 1.7481880
## 416 22.25 0.35218252     0.62838893               0.0000000 0.0000000
## 417 22.50 0.05115252     0.05115252               0.0000000 1.8512583
## 418 23.58 0.44560420     0.18752072               0.0000000 0.3010300
## 419 38.42 0.23172438     0.13830270               0.4771213 2.6998377
## 421 35.00 0.54406804     0.30103000               0.0000000 0.0000000
## 422 20.42 0.31910606     0.39794001               0.0000000 0.9030900
## 423 29.42 0.35218252     0.43933269               0.0000000 0.0000000
## 425 33.67 0.50037371     0.39794001               0.0000000 0.0000000
## 426 24.58 0.35218252     0.09691001               0.0000000 0.0000000
## 427 27.67 0.48287358     0.09691001               0.0000000 1.7075702
## 428 37.50 0.26363607     0.01703334               0.0000000 0.7781513
## 429 49.17 0.51719590     0.11058971               0.0000000 0.6020600
## 430 33.58 0.12548127     0.03542974               0.0000000 0.0000000
## 433 21.83 0.40483372     0.03542974               0.0000000 0.0000000
## 434 25.25 0.30103000     0.17609126               0.0000000 0.0000000
## 435 58.58 0.56937391     0.53339071               0.0000000 0.0000000
## 436 19.00 0.00000000     0.00000000               0.6989700 0.3010300
## 437 19.58 0.20002927     0.00000000               0.6020600 2.8864907
## 438 53.33 0.06632593     0.00000000               0.0000000 1.4471580
## 439 27.17 0.35218252     0.00000000               0.3010300 2.4785665
## 440 25.92 0.27300127     0.13830270               0.4771213 0.6020600
## 441 23.08 0.00000000     0.30103000               1.0791812 0.0000000
## 442 39.58 0.77815125     0.00000000               0.4771213 0.3010300
## 443 30.58 0.56937391     0.05115252               0.0000000 0.0000000
## 444 17.25 0.60205999     0.01703334               0.0000000 1.6127839
## 447 16.50 0.05115252     0.06632593               0.0000000 0.0000000
## 448 27.33 0.42569721     0.00000000               0.0000000 0.3010300
## 449 31.25 0.32735893     0.00000000               0.3010300 1.3010300
## 452 39.50 0.41912931     0.39794001               0.0000000 2.5010593
## 455 52.42 0.39794001     0.67669361               0.0000000 2.5453071
## 456 36.17 1.28160144     0.03542974               0.0000000 3.5505952
## 461 24.50 0.53339071     0.00000000               0.0000000 0.0000000
## 462 24.08 0.27300127     0.03542974               0.6989700 3.2902573
## 464 36.58 0.11058971     0.00000000               1.0413927 1.2787536
## 465 23.00 0.45255306     0.00000000               0.3010300 1.7323938
## 466 27.58 0.60205999     0.57863921               0.3010300 1.0413927
## 467 31.08 0.61119206     0.54406804               0.4771213 1.6232493
## 469 22.08 0.52309584     0.24303805               0.0000000 0.0000000
## 470 16.33 0.70629096     0.15075644               0.0000000 0.0000000
## 471 21.92 1.10260519     0.03542974               0.0000000 0.7781513
## 472 21.08 0.70969387     0.01703334               0.0000000 2.0043214
## 473 17.42 0.87506126     0.05115252               0.0000000 2.0043214
## 474 19.17 0.69897000     0.30103000               0.0000000 3.0004341
## 475 20.67 0.15075644     0.05115252               0.0000000 1.6532125
## 477 23.58 0.26363607     0.03542974               0.0000000 0.7781513
## 478 39.17 0.54406804     1.04139269               0.0000000 0.0000000
## 479 22.75 1.09691001     0.15075644               0.0000000 0.0000000
## 481 16.92 0.17609126     0.06632593               0.8450980 1.5563025
## 482 23.50 0.61961501     0.15075644               0.3010300 1.9084850
## 483 17.33 1.02118930     0.43933269               1.0413927 1.0413927
## 484 23.75 0.15075644     0.01703334               0.4771213 0.8450980
## 485 34.67 0.31806333     0.33545790               0.0000000 0.0000000
## 486 74.83 1.30103000     0.01703334               0.4771213 2.5465427
## 487 28.17 0.05115252     0.03542974               0.0000000 3.3224261
## 488 24.50 1.15639770     0.01703334               0.0000000 2.6776070
## 489 18.83 0.65705585     0.00000000               0.0000000 0.3010300
## 491 47.25 0.24303805     0.57403127               0.3010300 2.9508515
## 493 39.25 1.02118930     0.87506126               1.1760913 3.6635125
## 494 20.50 1.10839587     0.84509804               0.0000000 0.0000000
## 496 19.17 1.02118930     0.39794001               0.0000000 3.3438023
## 497 25.00 0.27300127     0.30963017               0.0000000 3.7679717
## 498 20.17 1.01072387     0.42569721               0.6020600 1.4623980
## 499 25.75 0.17609126     0.39093511               0.7781513 0.0000000
## 500 20.42 0.90308999     0.41912931               0.6020600 3.1436392
## 502 39.00 0.77815125     0.65321251               1.0413927 0.0000000
## 503 64.08 0.06632593     0.00000000               0.3010300 2.0043214
## 504 28.25 0.78710609     0.75966784               0.4771213 0.9030900
## 505 28.75 0.67669361     0.31910606               0.3010300 0.0000000
## 507 18.92 1.00000000     0.24303805               0.4771213 2.7723217
## 508 24.75 0.60205999     0.45255306               1.3010300 2.6998377
## 509 30.67 1.11394335     0.47712125               0.3010300 1.3010300
## 511 13.75 0.69897000     0.43933269               0.4771213 3.0004341
## 512 46.00 0.69897000     0.00000000               0.0000000 2.9827234
## 513 44.33 0.00000000     0.54406804               0.0000000 0.0000000
## 514 20.25 1.03981055     0.00000000               0.0000000 0.0000000
## 515 22.67 0.54900326     0.55448916               0.0000000 0.0000000
## 518 16.08 0.24303805     0.43933269               0.7781513 2.8394780
## 519 28.17 0.13830270     0.20002927               0.6989700 0.0000000
## 520 39.17 0.43296929     0.05115252               0.7781513 0.0000000
## 522 30.00 0.79865065     0.51188336               0.7781513 2.6998377
## 523 22.83 0.60205999     0.35983548               0.3010300 2.9036325
## 524 22.50 0.97772361     0.43933269               1.0413927 2.9960737
## 525 28.58 0.42569721     0.53339071               0.0000000 0.0000000
## 526 45.17 0.39794001     0.54406804               0.0000000 0.0000000
## 527 41.58 0.43933269     0.08278537               0.0000000 0.0000000
## 528 57.08 0.12548127     0.30103000               0.0000000 3.3420277
## 529 55.75 0.90741136     0.88930170               0.6020600 1.7075702
## 531 25.33 0.48925517     0.57403127               0.0000000 0.3010300
## 533 43.17 0.51188336     0.24303805               0.0000000 0.0000000
## 534 40.92 0.26363607     0.00000000               0.0000000 0.3010300
## 535 31.83 0.54406804     0.92941893               0.0000000 0.0000000
## 536 33.92 0.41246055     0.00000000               0.0000000 0.0000000
## 537 24.92 0.35218252     0.00000000               0.0000000 0.0000000
## 538 35.25 0.61961501     0.67669361               0.0000000 0.0000000
## 541 19.42 0.39794001     0.47712125               0.0000000 1.3222193
## 544 36.33 0.68033551     0.33545790               0.0000000 0.0000000
## 545 30.08 0.30963017     0.17609126               1.0413927 1.4623980
## 547 23.58 0.16435286     0.55930801               0.8450980 2.5415792
## 548 23.92 0.39794001     0.45863785               0.8450980 2.5158738
## 549 33.17 0.30103000     0.24303805               0.9030900 3.6098078
## 550 48.33 1.11394335     1.23044892               0.0000000 0.0000000
## 551 76.75 1.36716949     1.13830270               0.3010300 2.0413927
## 553 34.75 1.20411998     0.80448019               1.0000000 2.1303338
## 554 38.58 0.63698910     0.69897000               1.1760913 3.1287223
## 555 22.42 1.08813609     0.24303805               0.6989700 2.5078559
## 556 41.92 0.15228834     0.08278537               0.8450980 2.9772662
## 557 29.58 0.74036269     0.92941893               0.4771213 0.0000000
## 559 51.42 0.01703334     0.01703334               0.0000000 3.4772660
## 560 22.83 0.51719590     0.51719590               0.9030900 3.3774884
## 561 25.00 1.12483015     0.65321251               0.8450980 2.6618127
## 562 26.75 0.32735893     0.35218252               0.0000000 3.7241939
## 563 23.33 0.39794001     0.38291714               0.0000000 2.3031961
## 564 24.42 1.12499302     0.41246055               0.0000000 0.0000000
## 565 42.17 0.78103694     1.13830270               0.0000000 0.0000000
## 566 20.83 0.60205999     0.01703334               0.0000000 0.0000000
## 567 23.08 1.09691001     0.49485002               1.0791812 2.4548449
## 569 43.08 0.13830270     0.13830270               0.9542425 2.2121876
## 570 35.75 0.28216878     0.24303805               0.6989700 3.1997552
## 571 59.50 0.57403127     0.43933269               0.7781513 1.7708520
## 572 21.00 0.60205999     0.31910606               0.9542425 0.3010300
## 574 65.17 1.17609126     0.00000000               1.0791812 3.1464381
## 575 20.33 1.04139269     0.30103000               0.6989700 3.1661340
## 577 30.17 0.17609126     0.43933269               1.0791812 2.7331973
## 578 25.17 0.84509804     0.30103000               0.6020600 0.0000000
## 579 39.17 0.41912931     0.39794001               1.0413927 3.6721903
## 580 39.08 0.84509804     0.35983548               0.7781513 3.0406023
## 581 31.67 0.26245109     0.36828688               0.9542425 3.5173279
## 582 41.00 0.01703334     0.01703334               0.3010300 0.0000000
## 583 48.50 0.72015930     0.05115252               0.0000000 0.0000000
## 585 28.08 1.20411998     0.00000000               0.0000000 4.1210014
## 586 73.42 1.27300127     0.00000000               0.0000000 0.0000000
## 587 64.08 1.32221929     1.26717173               1.0000000 3.0004341
## 589 26.67 0.43933269     0.30103000               0.7781513 3.7617775
## 590 25.33 0.19865709     0.11058971               0.9030900 3.7096939
## 591 30.17 0.87506126     0.61542395               0.9542425 3.0795430
## 592 27.00 0.24303805     0.72015930               0.6020600 2.1789769
## 596 25.75 0.24303805     0.09691001               0.0000000 1.3802112
## 598 21.50 0.84509804     0.54406804               0.6020600 2.9633155
## 600 20.50 0.53339071     0.47712125               1.0791812 3.4772660
## 601 29.50 0.16435286     0.18752072               0.6989700 2.6998377
## 603 29.83 0.35218252     0.09691001               0.0000000 0.0000000
## 604 20.08 0.09691001     0.05115252               0.0000000 0.0000000
## 605 23.42 0.20002927     0.03542974               0.0000000 0.0000000
## 606 29.58 0.43933269     0.35218252               0.0000000 0.0000000
## 607 16.17 0.01703334     0.01703334               0.0000000 0.0000000
## 608 32.33 0.65321251     0.17609126               0.0000000 0.0000000
## 610 47.83 0.71307033     0.03542974               0.0000000 0.0000000
## 612 27.58 0.62838893     0.78426058               0.4771213 0.3010300
## 613 22.00 0.25285303     0.11058971               0.3010300 2.4533183
## 617 22.67 0.24303805     0.41246055               0.3010300 1.0000000
## 618 32.25 1.17609126     0.00000000               0.4771213 0.3010300
## 619 29.58 0.75966784     0.47712125               0.3010300 1.8388491
## 620 18.42 1.05747592     0.05115252               0.0000000 2.5751878
## 621 22.17 0.51188336     0.05115252               0.0000000 1.0413927
## 622 22.67 0.06632593     0.51188336               0.0000000 0.0000000
## 624 18.83 0.00000000     0.22141424               0.0000000 0.3010300
## 625 21.58 0.25285303     0.22141424               0.0000000 0.0000000
## 626 23.75 1.11394335     0.48925517               0.0000000 0.0000000
## 628 36.08 0.54900326     0.00000000               0.0000000 3.0004341
## 632 27.25 0.11058971     0.05115252               0.3010300 2.0374265
## 635 23.75 0.23299611     0.09691001               0.3010300 0.6989700
## 636 18.17 0.53907610     0.29225607               0.4771213 2.7693773
## 638 19.50 1.02469086     0.25285303               0.0000000 2.5453071
## 639 28.58 0.66511174     0.09691001               0.0000000 0.0000000
## 640 35.58 0.24303805     0.39794001               0.0000000 0.0000000
## 641 34.17 0.57403127     0.54406804               0.0000000 2.3031961
## 644 52.50 0.90308999     0.60205999               0.0000000 0.0000000
## 645 36.17 0.15228834     0.11058971               0.0000000 0.4771213
## 646 37.33 0.56407398     0.06632593               0.0000000 2.7007037
## 647 20.83 0.97772361     0.06632593               0.0000000 2.5465427
## 648 24.08 1.00000000     0.09691001               0.0000000 0.0000000
## 649 25.58 0.12548127     0.65321251               0.0000000 0.0000000
## 650 35.17 0.67669361     0.00000000               0.8450980 2.3031961
## 651 48.08 0.67669361     0.30103000               0.0000000 0.4771213
## 654 21.50 1.09691001     0.17609126               0.0000000 1.8388491
## 655 23.58 0.26245109     0.15075644               0.3010300 1.0791812
## 656 21.08 0.77815125     0.00000000               0.0000000 0.0000000
## 659 15.75 0.13830270     0.30103000               0.0000000 1.2787536
## 661 22.25 1.00000000     0.03542974               0.0000000 0.0000000
## 665 31.08 0.39794001     0.01703334               0.0000000 0.0000000
## 666 31.83 0.01703334     0.01703334               0.0000000 0.0000000
## 667 21.75 1.10551018     0.09691001               0.0000000 0.0000000
## 668 17.92 0.18752072     0.43933269               0.3010300 0.7781513
## 669 30.33 0.17609126     0.03542974               0.0000000 0.0000000
## 670 51.83 0.48287358     0.39794001               0.0000000 0.3010300
## 671 47.17 0.83473852     0.81291336               0.0000000 2.1789769
## 672 25.83 1.14097916     0.17609126               0.0000000 0.4771213
## 673 50.25 0.26363607     0.17609126               0.0000000 2.0718820
## 675 37.33 0.54406804     0.08278537               0.0000000 2.3926970
## 677 30.58 1.06688474     0.03542974               1.1139434 0.6020600
## 678 19.42 0.91645395     0.01703334               0.3010300 0.3010300
## 679 17.92 1.04960561     0.00000000               0.0000000 1.7075702
## 680 20.08 0.35218252     0.00000000               0.0000000 0.0000000
## 681 19.50 0.11058971     0.11058971               0.0000000 2.5622929
## 682 27.83 0.30103000     0.60205999               0.0000000 2.7307823
## 683 17.08 0.63245729     0.12548127               0.0000000 0.4771213
## 684 36.42 0.24303805     0.20002927               0.0000000 0.6020600
## 686 21.08 1.04473570     0.35218252               0.0000000 0.0000000
## 687 22.67 0.24303805     0.47712125               0.4771213 2.5965971
## 688 25.25 1.16136800     0.47712125               0.3010300 0.3010300
## 689 17.92 0.08098705     0.01703334               0.0000000 2.8756399
## 690 35.00 0.64097806     0.96801571               0.0000000 0.0000000

Mediante un gráfico de dispersión vemos las distribuciones de las dos primerascomponentes calculadas diferenciando según Aprobado.

pca_res <- prcomp(pca, scale = TRUE)
pca_df = as.data.frame(pca_res$x,stringsAsFactors=F)
pca_df = cbind(Aprobado = subset_train$Aprobado,pca_df)

ggplot(pca_df, aes(PC1, PC2)) + 
     modelr::geom_ref_line(h = 0) +
     modelr::geom_ref_line(v = 0) +
     geom_point(aes(color = Aprobado), size = 2, position='jitter',alpha = 0.4) +
     xlab("First Principal Component") + 
     ylab("Second Principal Component") + 
     ggtitle("First Two Principal Components of Credit Data")

Como vemos, la componente PC1 parece un buen predictor. Podemos visualizar su dispersión en el siguiente gráfico y utilizar su matriz de confusión para ver su rendimiento como predictor.

    ggplot(pca_df, aes(x = PC1, fill = Aprobado)) +
    geom_density(alpha = 0.5) +
    labs(title = "Distribución de PC1 según Aprobado",
        x = "PC1",
        y = "Densidad") +
    theme_minimal()

    pca_df$Prediccion_simple <- ifelse(pca_df$PC1 > 0.2, "Aprobado", "Rechazado")
    
    # Crear tabla de confusión en formato largo
    conf_mat <- table(Real = pca_df$Aprobado, Predicho = pca_df$Prediccion_simple)
    conf_df <- as.data.frame(conf_mat)

    # Añadir etiquetas
    conf_df <- conf_df %>%
        mutate(Tipo = case_when(
        Real == "Aprobado" & Predicho == "Aprobado" ~ "Verdadero Positivo",
        Real == "Rechazado" & Predicho == "Aprobado" ~ "Falso Positivo",
        Real == "Rechazado" & Predicho == "Rechazado" ~ "Verdadero Negativo",
        Real == "Aprobado" & Predicho == "Rechazado" ~ "Falso Negativo"
        ))

    ggplot(conf_df, aes(x = Predicho, y = Real, fill = Freq)) +
    geom_tile(color = "white") +
    geom_text(aes(label = paste0(Freq, "\n", Tipo)), size = 4) +
    scale_fill_gradient(low = "white", high = "steelblue") +
    labs(title = "Matriz de confusión para predictor basado en esfuerzo financiero",
        x = "Predicción", y = "Clase real", fill = "Frecuencia") +
    theme_minimal()

De la componente PC1 podemos saber también cómo se calcula a partir de los campos numéricos originales:

    loadings <- pca_res$rotation
    loadings[,1]
##                    Edad                   Deuda          Anos_cotizados 
##               0.4055650               0.4086410               0.5053226 
## Calificacion_crediticia                Ingresos 
##               0.5200229               0.3778291

Las seis componentes aparecen ordenadas según la proporción de la varianza explicada (PVE). La PVE de un componente coincide con la proporción del autovalor m-ésimo

5 Modelado y entrenamiento

– ¿Por qu´e has escogido estudiar ´este o aquel modelo frente a otros potencialmente alternativos? – ¿Has identificado y explicado cada uno de los hiper-par´ametros de configuraci´on a explorar de los modelos escogidos (m´ınimo los que explora caret para ese modelo)? – ¿Por qu´e has escogido probar esos valores espec´ıficos de ´este o aqu´el hiperpar´ametro? – ¿Has experimentado con hiperpar´ametros “ocultos” que no ofrece directamente caret? P.e. el par´ametro ntree de Random Forest (la implementaci´on rf solo permite modificar mtry, pero ntree es tambi´en muy influyente). Ver secci´on 11.2.5 del tutorial caretML.pdf. – ¿Has seguido alguna estrategia para la generaci´on del grid de valores de los hiperpar´ametros a explorar? ¿Las has detallado? ¿La has justificado? 10 – ¿Has hecho exploraciones m´as profundas/varios ciclos de ´este o aqu´el hiperpar´ametro afinando el grano? (P.e.: 100, 200, 300, 400, … y luego 125, 150, 175, 200, 225, 250, 275, … al detectar un pico en 200) ¿Ha funcionado el ajuste de grano m´as fino

5.1 Modelos seleccionados

Seleccionamos la variable que vamos a usar como salida (Aprobado) y el resto se utilizarán como entrada.

credit_esfuerzo <- credit.Datos.Train
credit_esfuerzo$Esfuerzo_financiero <- credit.Datos.Train$Deuda + credit.Datos.Train$Anos_cotizados  + abs(credit.Datos.Train$Ingresos-1)*0.375

credit_no_na<- na.omit(credit.Datos.Train)
credit_no_na_esfuerzo <- na.omit(credit_esfuerzo)
credit.Var.Salida.Usada <- c("Aprobado")
credit.Vars.Entrada.Usadas <- setdiff(names(credit.Datos.Train), c(credit.Var.Salida.Usada))
credit.Vars.Entrada.Usadas2 <- setdiff(names(credit_esfuerzo), credit.Var.Salida.Usada)

Probamos el preprocesado imputando por la mediana (numericas) y la moda (categóricas)

# Copia del dataset original
df_rf <- credit.Datos.Train

# Asegurarse de que la variable de salida es factor
df_rf[[credit.Var.Salida.Usada]] <- as.factor(df_rf[[credit.Var.Salida.Usada]])

# 1. Variables numéricas: imputar con la mediana
vars_numericas <- names(df_rf)[sapply(df_rf, is.numeric)]
for (var in vars_numericas) {
  if (anyNA(df_rf[[var]])) {
    med <- median(df_rf[[var]], na.rm = TRUE)
    df_rf[[var]][is.na(df_rf[[var]])] <- med
  }
}

# 2. Variables categóricas: imputar con la moda
vars_categoricas <- names(df_rf)[sapply(df_rf, is.character) | sapply(df_rf, is.factor)]
moda <- function(x) names(which.max(table(x)))
for (var in vars_categoricas) {
  if (anyNA(df_rf[[var]])) {
    mod <- moda(df_rf[[var]][!is.na(df_rf[[var]])])
    df_rf[[var]][is.na(df_rf[[var]])] <- mod
  }
  df_rf[[var]] <- as.factor(df_rf[[var]])
}

# Resultado: df_rf ya tiene todos los NA imputados
sum(is.na(df_rf))  # debe ser 0
## [1] 0

5.1.1 rpart

Como primera aproximación a la tarea de clasificación, nos pareció interesante usar una técnica sencilla como lo son los árboles de decisión. El objetivo de este ataque, más que obtener una solución final, es establecer un clasificador base contra el que contrastar resultados porsteriores que obtendremos con otros modelos.

library(rpart)

set.seed(155)

tree_model <- train(
  credit_no_na[credit.Vars.Entrada.Usadas],
  credit_no_na[[credit.Var.Salida.Usada]],
  method = "rpart",
  metric = "Accuracy",
  tuneLength = 10
)

tree_model
## CART 
## 
## 516 samples
##  16 predictor
##   2 classes: 'Aprobado', 'Rechazado' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 516, 516, 516, 516, 516, 516, ... 
## Resampling results across tuning parameters:
## 
##   cp          Accuracy   Kappa    
##   0.00000000  0.8441161  0.6867780
##   0.07943262  0.8713235  0.7439150
##   0.15886525  0.8713235  0.7439150
##   0.23829787  0.8713235  0.7439150
##   0.31773050  0.8713235  0.7439150
##   0.39716312  0.8713235  0.7439150
##   0.47659574  0.8713235  0.7439150
##   0.55602837  0.8713235  0.7439150
##   0.63546099  0.8583286  0.7128778
##   0.71489362  0.6532674  0.2610764
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.5560284.
# rpart.plot(tree_model$finalModel, type = 2, extra = 104, fallen.leaves = TRUE, main = "Árbol de Decisión (rpart)")
varImp(tree_model)
## rpart variable importance
## 
##                         Overall
## Impago_previo            100.00
## Calificacion_crediticia   42.91
## Trabaja                   34.46
## Anos_cotizados            27.50
## Ingresos                  26.01
## Licencia_de_conducir       0.00
## DeudaRelativa              0.00
## Etnia                      0.00
## Deuda                      0.00
## Es_cliente                 0.00
## Edad                       0.00
## Codigo_postal              0.00
## Nivel_educativo            0.00
## Estado_civil               0.00
## Ciudadano                  0.00
## Sexo                       0.00
tree_model$finalModel$variable.importance
##           Impago_previo Calificacion_crediticia                 Trabaja 
##               143.67623                55.35068                55.35068 
##          Anos_cotizados         Nivel_educativo                Ingresos 
##                47.69580                34.15255                33.56371

Hemos hecho uso de rpart, con un tuneLength de 10. Hemos probado distintos valores de este hiperparámetro, pero los resultados que se obtienen son casi siempre los mismos. Con este método, el único hiperpárametro a modificar es el de complejidad cp, que toma valores entre 0 y 1. Pues bien, para todo valor entre 0.018 y 0.714, el árbol que se obtiene es exactamente el mismo.

Evaluando los resultados obtenidos, nos damos cuenta que el árbol encontrado con mejor accuracy no es otro que el decisor que ya habíamos considerado en la fase de análisis exploratorio: si existe impago previo se rechaza la solicitud, si no, se aprueba.

5.1.2 Random Forest (ranger)

El siguiente modelo que vamos a utilizar es Random Forest. Los Bosques Aleatorios son una técnica de aprendizaje automático que opera construyendo múltiples árboles de decisión durante el entrenamiento y generando la clase que es elegida con mayor frecuencia en todos los árboles individuales para la clasificación.

En concreto usamos ranger que está dentro de la librería caret. La razón principal para elegir ranger es su eficiencia. ranger es conocida por ser una de las implementaciones más rápidas de Bosques Aleatorios disponibles en R, o que nos permitirá entrenar modelos de Bosques Aleatorios en grandes conjuntos de datos de manera eficiente.

Los dos hiperparámetros más influyentes para optimizar el rendimiento del modelo son mtry y splitrule:

  • mtry: Controla el número de variables predictoras que se consideran aleatoriamente en cada división de un nodo al construir un árbol individual.

  • splitrule Define el criterio utilizado para decidir cómo se realiza la mejor división en cada nodo de un árbol. Las opciones disponibles para esta parámetro son:

    • gini (por defecto): Utiliza el índice de Gini para medir la impureza.

    • extratrees: Implementa el algoritmo de “Extremely Randomized Trees”. En lugar de buscar la mejor división para cada variable (como lo hace Gini), elige puntos de división aleatorios para cada variable y luego selecciona la mejor de estas divisiones aleatorias.

set.seed(68)
fitControl <- trainControl(## 10-fold CV
                           method = "cv",
                           number = 10)

credit.ranger <- train(
  credit_no_na[credit.Vars.Entrada.Usadas],  
  credit_no_na[[credit.Var.Salida.Usada]],   
  method = "ranger",                  
  verbose = FALSE,
  tuneLength = 15,                    
  trControl = fitControl,         
  num.trees = 500,                     
  importance = 'impurity'            
)

credit.ranger
## Random Forest 
## 
## 516 samples
##  16 predictor
##   2 classes: 'Aprobado', 'Rechazado' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 465, 465, 464, 465, 464, 465, ... 
## Resampling results across tuning parameters:
## 
##   mtry  splitrule   Accuracy   Kappa    
##    2    gini        0.8901050  0.7777133
##    2    extratrees  0.8839267  0.7668442
##    3    gini        0.8802285  0.7586462
##    3    extratrees  0.8820776  0.7632066
##    4    gini        0.8781560  0.7549213
##    4    extratrees  0.8820776  0.7633982
##    5    gini        0.8781560  0.7549213
##    5    extratrees  0.8820776  0.7631411
##    6    gini        0.8781560  0.7550735
##    6    extratrees  0.8840007  0.7670767
##    7    gini        0.8800805  0.7590754
##    7    extratrees  0.8840007  0.7670887
##    8    gini        0.8801168  0.7589251
##    8    extratrees  0.8820022  0.7630356
##    9    gini        0.8762330  0.7514862
##    9    extratrees  0.8820776  0.7635024
##   10    gini        0.8801545  0.7594055
##   10    extratrees  0.8838890  0.7667837
##   11    gini        0.8820776  0.7632164
##   11    extratrees  0.8820399  0.7632218
##   12    gini        0.8801545  0.7591819
##   12    extratrees  0.8877728  0.7747157
##   13    gini        0.8800805  0.7590754
##   13    extratrees  0.8801531  0.7593468
##   14    gini        0.8762330  0.7511104
##   14    extratrees  0.8858861  0.7707833
##   15    gini        0.8781937  0.7551780
##   15    extratrees  0.8858861  0.7705728
##   16    gini        0.8801908  0.7592560
##   16    extratrees  0.8819282  0.7628086
## 
## Tuning parameter 'min.node.size' was held constant at a value of 1
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were mtry = 2, splitrule = gini
##  and min.node.size = 1.
varImp(credit.ranger)
## ranger variable importance
## 
##                          Overall
## Impago_previo           100.0000
## Anos_cotizados           35.3045
## Calificacion_crediticia  29.3928
## DeudaRelativa            27.1785
## Ingresos                 26.1977
## Deuda                    23.9231
## Codigo_postal            21.1322
## Edad                     20.6178
## Nivel_educativo          17.8361
## Trabaja                  17.5723
## Etnia                     6.5909
## Estado_civil              1.6975
## Licencia_de_conducir      1.2229
## Es_cliente                1.0557
## Sexo                      0.8616
## Ciudadano                 0.0000
names_expected <- credit.ranger$finalModel$forest$independent.variable.names
print(names_expected)
##  [1] "Sexo"                    "Edad"                   
##  [3] "Deuda"                   "Estado_civil"           
##  [5] "Es_cliente"              "Nivel_educativo"        
##  [7] "Etnia"                   "Anos_cotizados"         
##  [9] "Impago_previo"           "Trabaja"                
## [11] "Calificacion_crediticia" "Licencia_de_conducir"   
## [13] "Ciudadano"               "Codigo_postal"          
## [15] "Ingresos"                "DeudaRelativa"
# Check the names of the variables present in your test data
names_in_test_data <- names(credit.Datos.Test)
print(names_in_test_data)
##  [1] "Sexo"                    "Edad"                   
##  [3] "Deuda"                   "Estado_civil"           
##  [5] "Es_cliente"              "Nivel_educativo"        
##  [7] "Etnia"                   "Anos_cotizados"         
##  [9] "Impago_previo"           "Trabaja"                
## [11] "Calificacion_crediticia" "Licencia_de_conducir"   
## [13] "Ciudadano"               "Codigo_postal"          
## [15] "Ingresos"                "Aprobado"               
## [17] "DeudaRelativa"
# Predecir sobre el conjunto de test usando el modelo entrenado
pred_ranger <- predict(credit.ranger, newdata = credit.Datos.Test[credit.Vars.Entrada.Usadas])

# Matriz de confusión y métricas de rendimiento
confusionMatrix(pred_ranger, credit.Datos.Test$Aprobado)
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  Aprobado Rechazado
##   Aprobado        54        12
##   Rechazado        7        64
##                                           
##                Accuracy : 0.8613          
##                  95% CI : (0.7919, 0.9144)
##     No Information Rate : 0.5547          
##     P-Value [Acc > NIR] : 1.464e-14       
##                                           
##                   Kappa : 0.7215          
##                                           
##  Mcnemar's Test P-Value : 0.3588          
##                                           
##             Sensitivity : 0.8852          
##             Specificity : 0.8421          
##          Pos Pred Value : 0.8182          
##          Neg Pred Value : 0.9014          
##              Prevalence : 0.4453          
##          Detection Rate : 0.3942          
##    Detection Prevalence : 0.4818          
##       Balanced Accuracy : 0.8637          
##                                           
##        'Positive' Class : Aprobado        
## 

6 Comparación de modelos

– ¿Hay alg´un modelo mejor que los dem´as? Justif´ıcalo. – ¿Hay alg´un modelo en particular que sea mejor que otro en particular? Justif´ıcalo. – ¿En base a qu´e criterio consideras que ´este o aquel modelo es mejor? Justif´ıcalo.

# Comparar métricas: resamples(), summary(), dotplot(), etc.

6.1 Comparación con distintos preprocesados

# Comparación de un modelo entrenado con dos tipos de datos preprocesados

7 Selección del modelo final

Justifica por qué se selecciona ese modelo final y qué rendimiento tiene sobre el conjunto de test.

# predict(), confusionMatrix(), estimación de accuracy final

8 Conclusiones

Resumen de hallazgos, aprendizajes y posibles mejoras.

Puedes incluir comentarios subjetivos sobre el proceso, dificultades, o ideas para prácticas futuras.

9 Anexos

sessionInfo()
## R version 4.4.2 (2024-10-31)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.1 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=es_ES.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=es_ES.UTF-8        LC_COLLATE=es_ES.UTF-8    
##  [5] LC_MONETARY=es_ES.UTF-8    LC_MESSAGES=es_ES.UTF-8   
##  [7] LC_PAPER=es_ES.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=es_ES.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Europe/Madrid
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] rpart_4.1.23    GGally_2.2.1    gridExtra_2.3   lubridate_1.9.3
##  [5] forcats_1.0.0   stringr_1.5.1   dplyr_1.1.4     purrr_1.0.2    
##  [9] readr_2.1.5     tidyr_1.3.1     tibble_3.2.1    tidyverse_2.0.0
## [13] caret_7.0-1     lattice_0.22-5  ggplot2_3.5.1  
## 
## loaded via a namespace (and not attached):
##  [1] tidyselect_1.2.1     timeDate_4041.110    farver_2.1.2        
##  [4] fastmap_1.2.0        pROC_1.18.5          digest_0.6.37       
##  [7] timechange_0.3.0     lifecycle_1.0.4      survival_3.8-3      
## [10] magrittr_2.0.3       compiler_4.4.2       rlang_1.1.4         
## [13] sass_0.4.9           tools_4.4.2          utf8_1.2.4          
## [16] yaml_2.3.10          data.table_1.16.2    knitr_1.49          
## [19] labeling_0.4.3       plyr_1.8.9           RColorBrewer_1.1-3  
## [22] withr_3.0.2          nnet_7.3-20          grid_4.4.2          
## [25] stats4_4.4.2         fansi_1.0.6          e1071_1.7-16        
## [28] colorspace_2.1-1     future_1.49.0        globals_0.18.0      
## [31] scales_1.3.0         iterators_1.0.14     MASS_7.3-64         
## [34] cli_3.6.3            rmarkdown_2.29       generics_0.1.3      
## [37] future.apply_1.11.3  modelr_0.1.11        reshape2_1.4.4      
## [40] tzdb_0.4.0           proxy_0.4-27         cachem_1.1.0        
## [43] splines_4.4.2        parallel_4.4.2       vctrs_0.6.5         
## [46] hardhat_1.4.1        Matrix_1.7-1         jsonlite_1.8.9      
## [49] hms_1.1.3            listenv_0.9.1        foreach_1.5.2       
## [52] gower_1.0.1          jquerylib_0.1.4      recipes_1.3.1       
## [55] glue_1.8.0           parallelly_1.44.0    ggstats_0.9.0       
## [58] codetools_0.2-19     stringi_1.8.4        gtable_0.3.6        
## [61] munsell_0.5.1        pillar_1.9.0         htmltools_0.5.8.1   
## [64] ipred_0.9-15         randomForest_4.7-1.2 lava_1.8.1          
## [67] R6_2.5.1             doParallel_1.0.17    evaluate_1.0.1      
## [70] backports_1.5.0      broom_1.0.7          bslib_0.8.0         
## [73] class_7.3-23         Rcpp_1.0.13-1        nlme_3.1-166        
## [76] prodlim_2025.04.28   ranger_0.17.0        xfun_0.49           
## [79] ModelMetrics_1.2.2.2 pkgconfig_2.0.3